<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
  <meta name="theme-color" content="#222">
  <meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/safari-pinned-tab.svg" color="#222">
  <link rel="stylesheet" href="/css/main.css">
  <link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="/lib/pace/pace-theme-minimal.min.css">
  <script src="/lib/pace/pace.min.js"></script>
  <script id="hexo-configurations">
    var NexT = window.NexT ||
    {};
    var CONFIG = {
      "hostname": "cuiqingcai.com",
      "root": "/",
      "scheme": "Pisces",
      "version": "7.8.0",
      "exturl": false,
      "sidebar":
      {
        "position": "right",
        "width": 360,
        "display": "post",
        "padding": 18,
        "offset": 12,
        "onmobile": false,
        "widgets": [
          {
            "type": "image",
            "name": "阿布云",
            "enable": false,
            "url": "https://www.abuyun.com/http-proxy/introduce.html",
            "src": "https://qiniu.cuiqingcai.com/88au8.jpg",
            "width": "100%"
      },
          {
            "type": "image",
            "name": "天验",
            "enable": true,
            "url": "https://tutorial.lengyue.video/?coupon=12ef4b1a-a3db-11ea-bb37-0242ac130002_cqx_850",
            "src": "https://qiniu.cuiqingcai.com/bco2a.png",
            "width": "100%"
      },
          {
            "type": "image",
            "name": "华为云",
            "enable": false,
            "url": "https://activity.huaweicloud.com/2020_618_promotion/index.html?bpName=5f9f98a29e2c40b780c1793086f29fe2&bindType=1&salesID=wangyubei",
            "src": "https://qiniu.cuiqingcai.com/y42ik.jpg",
            "width": "100%"
      },
          {
            "type": "image",
            "name": "张小鸡",
            "enable": false,
            "url": "http://www.zxiaoji.com/",
            "src": "https://qiniu.cuiqingcai.com/fm72f.png",
            "width": "100%"
      },
          {
            "type": "image",
            "name": "Luminati",
            "src": "https://qiniu.cuiqingcai.com/ikkq9.jpg",
            "url": "https://luminati-china.io/?affiliate=ref_5fbbaaa9647883f5c6f77095",
            "width": "100%",
            "enable": false
      },
          {
            "type": "image",
            "name": "IPIDEA",
            "url": "http://www.ipidea.net/?utm-source=cqc&utm-keyword=?cqc",
            "src": "https://qiniu.cuiqingcai.com/0ywun.png",
            "width": "100%",
            "enable": true
      },
          {
            "type": "tags",
            "name": "标签云",
            "enable": true
      },
          {
            "type": "categories",
            "name": "分类",
            "enable": true
      },
          {
            "type": "friends",
            "name": "友情链接",
            "enable": true
      },
          {
            "type": "hot",
            "name": "猜你喜欢",
            "enable": true
      }]
      },
      "copycode":
      {
        "enable": true,
        "show_result": true,
        "style": "mac"
      },
      "back2top":
      {
        "enable": true,
        "sidebar": false,
        "scrollpercent": true
      },
      "bookmark":
      {
        "enable": false,
        "color": "#222",
        "save": "auto"
      },
      "fancybox": false,
      "mediumzoom": false,
      "lazyload": false,
      "pangu": true,
      "comments":
      {
        "style": "tabs",
        "active": "gitalk",
        "storage": true,
        "lazyload": false,
        "nav": null,
        "activeClass": "gitalk"
      },
      "algolia":
      {
        "hits":
        {
          "per_page": 10
        },
        "labels":
        {
          "input_placeholder": "Search for Posts",
          "hits_empty": "We didn't find any results for the search: ${query}",
          "hits_stats": "${hits} results found in ${time} ms"
        }
      },
      "localsearch":
      {
        "enable": true,
        "trigger": "auto",
        "top_n_per_article": 10,
        "unescape": false,
        "preload": false
      },
      "motion":
      {
        "enable": false,
        "async": false,
        "transition":
        {
          "post_block": "bounceDownIn",
          "post_header": "slideDownIn",
          "post_body": "slideDownIn",
          "coll_header": "slideLeftIn",
          "sidebar": "slideUpIn"
        }
      },
      "path": "search.xml"
    };

  </script>
  <meta name="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
  <meta property="og:type" content="website">
  <meta property="og:title" content="静觅">
  <meta property="og:url" content="https://cuiqingcai.com/page/14/index.html">
  <meta property="og:site_name" content="静觅">
  <meta property="og:description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
  <meta property="og:locale" content="zh_CN">
  <meta property="article:author" content="崔庆才">
  <meta property="article:tag" content="崔庆才">
  <meta property="article:tag" content="静觅">
  <meta property="article:tag" content="PHP">
  <meta property="article:tag" content="Java">
  <meta property="article:tag" content="Python">
  <meta property="article:tag" content="Spider">
  <meta property="article:tag" content="爬虫">
  <meta property="article:tag" content="Web">
  <meta property="article:tag" content="Kubernetes">
  <meta property="article:tag" content="深度学习">
  <meta property="article:tag" content="机器学习">
  <meta property="article:tag" content="数据分析">
  <meta property="article:tag" content="网络">
  <meta property="article:tag" content="IT">
  <meta property="article:tag" content="技术">
  <meta property="article:tag" content="博客">
  <meta name="twitter:card" content="summary">
  <link rel="canonical" href="https://cuiqingcai.com/page/14/">
  <script id="page-configurations">
    // https://hexo.io/docs/variables.html
    CONFIG.page = {
      sidebar: "",
      isHome: true,
      isPost: false,
      lang: 'zh-CN'
    };

  </script>
  <title>静觅丨崔庆才的个人站点</title>
  <meta name="google-site-verification" content="p_bIcnvirkFzG2dYKuNDivKD8-STet5W7D-01woA2fc" />
  <noscript>
    <style>
      .use-motion .brand,
      .use-motion .menu-item,
      .sidebar-inner,
      .use-motion .post-block,
      .use-motion .pagination,
      .use-motion .comments,
      .use-motion .post-header,
      .use-motion .post-body,
      .use-motion .collection-header
      {
        opacity: initial;
      }

      .use-motion .site-title,
      .use-motion .site-subtitle
      {
        opacity: initial;
        top: initial;
      }

      .use-motion .logo-line-before i
      {
        left: initial;
      }

      .use-motion .logo-line-after i
      {
        right: initial;
      }

    </style>
  </noscript>
  <link rel="alternate" href="/atom.xml" title="静觅" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container">
    <div class="headband"></div>
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner">
        <div class="site-brand-container">
          <div class="site-nav-toggle">
            <div class="toggle" aria-label="切换导航栏">
              <span class="toggle-line toggle-line-first"></span>
              <span class="toggle-line toggle-line-middle"></span>
              <span class="toggle-line toggle-line-last"></span>
            </div>
          </div>
          <div class="site-meta">
            <a href="/" class="brand" rel="start">
              <span class="logo-line-before"><i></i></span>
              <h1 class="site-title">静觅 <span class="site-subtitle"> 崔庆才的个人站点 </span>
              </h1>
              <span class="logo-line-after"><i></i></span>
            </a>
          </div>
          <div class="site-nav-right">
            <div class="toggle popup-trigger">
              <i class="fa fa-search fa-fw fa-lg"></i>
            </div>
          </div>
        </div>
        <nav class="site-nav">
          <ul id="menu" class="main-menu menu">
            <li class="menu-item menu-item-home">
              <a href="/" rel="section">首页</a>
            </li>
            <li class="menu-item menu-item-archives">
              <a href="/archives/" rel="section">文章列表</a>
            </li>
            <li class="menu-item menu-item-tags">
              <a href="/tags/" rel="section">文章标签</a>
            </li>
            <li class="menu-item menu-item-categories">
              <a href="/categories/" rel="section">文章分类</a>
            </li>
            <li class="menu-item menu-item-about">
              <a href="/about/" rel="section">关于博主</a>
            </li>
            <li class="menu-item menu-item-message">
              <a href="/message/" rel="section">给我留言</a>
            </li>
            <li class="menu-item menu-item-search">
              <a role="button" class="popup-trigger">搜索 </a>
            </li>
          </ul>
        </nav>
        <div class="search-pop-overlay">
          <div class="popup search-popup">
            <div class="search-header">
              <span class="search-icon">
                <i class="fa fa-search"></i>
              </span>
              <div class="search-input-container">
                <input autocomplete="off" autocapitalize="off" placeholder="搜索..." spellcheck="false" type="search" class="search-input">
              </div>
              <span class="popup-btn-close">
                <i class="fa fa-times-circle"></i>
              </span>
            </div>
            <div id="search-result">
              <div id="no-result">
                <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
              </div>
            </div>
          </div>
        </div>
      </div>
    </header>
    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
      <span>0%</span>
    </div>
    <div class="reading-progress-bar"></div>
    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div class="content index posts-expand">
            <div class="carousel">
              <div id="wowslider-container">
                <div class="ws_images">
                  <ul>
                    <li><a target="_blank" href="https://cuiqingcai.com/5052.html"><img title="Python3网络爬虫开发实战教程" src="https://qiniu.cuiqingcai.com/ipy96.jpg" /></a></li>
                    <li><a target="_blank" href="https://t.lagou.com/fRCBRsRCSN6FA"><img title="52讲轻松搞定网络爬虫" src="https://qiniu.cuiqingcai.com/fqq5e.png" /></a></li>
                    <li><a target="_blank" href="https://brightdata.grsm.io/cuiqingcai"><img title="亮网络解锁器" src="https://qiniu.cuiqingcai.com/6qnb7.png" /></a></li>
                    <li><a target="_blank" href="https://cuiqingcai.com/4320.html"><img title="Python3网络爬虫开发视频教程" src="https://qiniu.cuiqingcai.com/bjrny.jpg" /></a></li>
                    <li><a target="_blank" href="https://cuiqingcai.com/5094.html"><img title="爬虫代理哪家强？十大付费代理详细对比评测出炉！" src="https://qiniu.cuiqingcai.com/nifs6.jpg" /></a></li>
                  </ul>
                </div>
                <div class="ws_thumbs">
                  <div>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/ipy96.jpg" /></a>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/fqq5e.png" /></a>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/6qnb7.png" /></a>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/bjrny.jpg" /></a>
                    <a target="_blank" href="#"><img src="https://qiniu.cuiqingcai.com/nifs6.jpg" /></a>
                  </div>
                </div>
                <div class="ws_shadow"></div>
              </div>
            </div>
            <link rel="stylesheet" href="/lib/wowslide/slide.css">
            <script src="/lib/wowslide/jquery.min.js"></script>
            <script src="/lib/wowslide/slider.js"></script>
            <script>
              jQuery("#wowslider-container").wowSlider(
              {
                effect: "cube",
                prev: "",
                next: "",
                duration: 20 * 100,
                delay: 20 * 100,
                width: 716,
                height: 297,
                autoPlay: true,
                playPause: true,
                stopOnHover: false,
                loop: false,
                bullets: 0,
                caption: true,
                captionEffect: "slide",
                controls: true,
                onBeforeStep: 0,
                images: 0
              });

            </script>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6730.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> JavaScript <i class="label-arrow"></i>
                  </a>
                  <a href="/6730.html" class="post-title-link" itemprop="url">滑动验证码的原理并利用 Vue 实现滑动验证码</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>做网络爬虫的同学肯定见过各种各样的验证码，比较高级的有滑动、点选等样式，看起来好像挺复杂的，但实际上它们的核心原理还是还是很清晰的，本文章大致说明下这些验证码的原理以及带大家实现一个滑动验证码。 我之前做过 Web 相关开发，尝试对接过 Lavavel 的极验验证，当时还开发了一个 Lavavel 包：<a href="https://github.com/Germey/LaravelGeetest" target="_blank" rel="noopener">https://github.com/Germey/LaravelGeetest</a>，在开发包的过程中了解到了验证码的两步校验规则。 实际上这类验证码的校验是分为两个步骤的：</p>
                  <ol>
                    <li>第一步就是前端的校验。一般来说，登录注册页面在点击提交的时候都会伴随着一个表单提交，在表单提交的时候会有 JavaScript 事件的触发。如果加入了验证码，那么在表单提交的时候会多加一个额外的验证，判断这个验证码是否已经成功完成了操作。如果没有的话，那就直接取消表单的提交，然后顺便提示说”您的验证没通过，请重新验证“，诸如此类的话。所以这一步就能防范”君子“只为了。</li>
                    <li>第二步就是服务端的校验。意思就是说表单提交之后，会有请求发送到服务器，这个请求中包含了很多数据，比如用户名、密码，如果对接了验证码的话，还会有额外的验证码的值，或者更复杂的加密后的 Token 值，服务器会对发过来的信息进行校验，如果验证通过，那么整个请求就成功了，返回正常的响应，否则返回错误的响应。所以如果想要通过程序来直接构造表单提交的时候，服务端就可以做进一步的校验，由于提交的验证码相关的信息都是和服务端的 Session 相关联的，另外再加上一些 CSRF 等的校验，所以这一步就能防范”小人“之为了。</li>
                  </ol>
                  <p>上面就是验证码校验的两个阶段，一般来说为了安全性，在开发一个网站时需要客户端和服务端都加上校验，这样才能保证安全性。 本文章主要来介绍一下第一个阶段，也就是前端校验的验证码的实现，下面来介绍一下拖动验证码的具体实现。</p>
                  <h2 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h2>
                  <p>那么前端完成一个合格的验证码，究竟需要做成什么样子呢？</p>
                  <ol>
                    <li>首先验证码有个大体的雏形，既然是拖动验证码，那就要拖动块和目标块，我们需要把拖动块拖动到目标块上就算校验成功。</li>
                    <li>验证码的一个功能就是来规避机器的自动操作，所以我们需要通过轨迹来判断这个拖动过程是真实的人还是机器，因此我们需要记录拖动的路径，路径经过计算之后可以发送到后端进行进一步的分类，比如对接深度学习模型来分类拖动轨迹是否是人。</li>
                  </ol>
                  <p>以上就是验证码的两个基本要求，所以我们这里就来实现一下看看。</p>
                  <h2 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h2>
                  <p>这里就先给大家看看结果吧： <img src="https://qiniu.cuiqingcai.com/2019-06-26-061556.gif" alt="拖动验证码示例"> 可以看到图中有一个初始滑块，有一个目标滑块，如果把初始滑块拖动到目标滑块上才能校验成功，然后下方再打印拖动的轨迹，包含它的 x、y 坐标。 有了这些内容之后，就可以放到表单里面进行提交了，轨迹数据可以自行加密处理并校验来判断其是否合法。</p>
                  <h2 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h2>
                  <p>下面就具体讲解下这个是怎么实现的，实际上核心代码只有 200 行，下面对整个核心流程进行说明。 既然 Vue 这么火，那我这里就用 Vue 来实现啦，具体的环境配置这里就不再赘述了，需要安装的有：</p>
                  <ul>
                    <li>Node.js：<a href="https://nodejs.org/en/" target="_blank" rel="noopener">https://nodejs.org/en/</a></li>
                    <li>Vue-Cli：<a href="https://cli.vuejs.org/zh/" target="_blank" rel="noopener">https://cli.vuejs.org/zh/</a></li>
                  </ul>
                  <p>安装完成之后便可以使用 vue 命令了，新建个项目：</p>
                  <figure class="highlight ebnf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attribute">vue create drag-captcha</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>然后找一张不错的风景图，放到 public 目录下，后面我们会引用它。 另外这里需要一个核心的包叫做 vue-drag-drop，其 GitHub 地址为：<a href="https://github.com/cameronhimself/vue-drag-drop" target="_blank" rel="noopener">https://github.com/cameronhimself/vue-drag-drop</a>，在目录下使用此命令安装：</p>
                  <figure class="highlight gauss">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">npm install --<span class="keyword">save</span> vue-drag-<span class="built_in">drop</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>安装好了之后我们就可以利用它来实现验证码了。 首先 vue-drag-drop 提供了两个组件，一个叫做 Drag，一个叫做 Drop。前者是被拖动对象，后者是放置目标，我们利用这两个组件构建两个滑块，将 Drag 滑块拖动到 Drop 滑块上就成功了。因此，我们要做的仅仅是把它们两个声明出来并添加几个检测方法就好了，至于拖动的功能，vue-drag-drop 这个组件已经给我们封装好了。 这里我们就直接在 App.vue 里面修改内容就好了，在 <code>&lt;template&gt;</code> 里面先声明一下两个组件：</p>
                  <figure class="highlight applescript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;<span class="keyword">div</span> <span class="built_in">id</span>=<span class="string">"app"</span>&gt;</span><br><span class="line">    &lt;<span class="keyword">div</span> <span class="built_in">id</span>=<span class="string">"wrapper"</span> :style=<span class="string">"wrapperStyle"</span>&gt;</span><br><span class="line">      &lt;drop <span class="built_in">class</span>=<span class="string">"drop"</span> <span class="built_in">id</span>=<span class="string">"target"</span></span><br><span class="line">            :<span class="built_in">class</span>=<span class="string">"&#123; 'over': state.over &#125;"</span></span><br><span class="line">            @dragover=<span class="string">"onDragOver"</span></span><br><span class="line">            @dragleave=<span class="string">"onDragLeave"</span></span><br><span class="line">            :style=<span class="string">"targetStyle"</span>&gt;</span><br><span class="line">      &lt;/drop&gt;</span><br><span class="line">      &lt;drag <span class="built_in">class</span>=<span class="string">"drag"</span> <span class="built_in">id</span>=<span class="string">"source"</span></span><br><span class="line">            :transfer-data=<span class="string">"true"</span></span><br><span class="line">            @dragstart=<span class="string">"onDragStart"</span></span><br><span class="line">            @dragend=<span class="string">"onDragEnd"</span></span><br><span class="line">            @drag=<span class="string">"onDrag"</span> v-<span class="keyword">if</span>=<span class="string">"!state.dragged"</span></span><br><span class="line">            :style=<span class="string">"sourceStyle"</span>&gt;</span><br><span class="line">        &lt;<span class="keyword">div</span> slot=<span class="string">"image"</span> <span class="built_in">id</span>=<span class="string">"float"</span> :style=<span class="string">"sourceStyle"</span>&gt;</span><br><span class="line">        &lt;/<span class="keyword">div</span>&gt;</span><br><span class="line">      &lt;/drag&gt;</span><br><span class="line">    &lt;/<span class="keyword">div</span>&gt;</span><br><span class="line">  &lt;/<span class="keyword">div</span>&gt;</span><br><span class="line">&lt;/template&gt;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>很清晰了，一个 <code>&lt;drop&gt;</code> 和一个 <code>&lt;drag&gt;</code> 组件，里面绑定了一些属性，下面对这两个组件的常用属性作一下说明。</p>
                  <h3 id="Drop"><a href="#Drop" class="headerlink" title="Drop"></a>Drop</h3>
                  <p>对于 Drop 组件来说，它是一个被放置的对象，被拖动滑块会放到这个 Drop 滑块上，这就代表拖动成功了。它有两个主要的事件需要监听，一个叫做 dragover，一个叫做 dragleave，分别用来监听 Drag 对象拖上和拖开的事件。 在这里，分别对两个事件设置了 onDragOver 和 onDragLeave 的回调函数，当 Drag 对象放到 Drop 对象上面的时候，就会触发 onDragOver 对象，当拖开的时候就会触发 onDragLeave 事件。 那这样的话我们只需要一个全局变量来记录是否已经将滑块拖动到目标位置即可，比如可以定一个全局变量 state，我们用 over 属性来代表是否拖动到目标位置。 因此 onDragOver 和 onDragLeave 事件可以这么实现：</p>
                  <figure class="highlight pf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">on</span>DragOver() &#123;</span><br><span class="line">    this.<span class="keyword">state</span>.over = true</span><br><span class="line">&#125;,</span><br><span class="line"><span class="keyword">on</span>DragLeave() &#123;</span><br><span class="line">    this.<span class="keyword">state</span>.over = false</span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="Drag"><a href="#Drag" class="headerlink" title="Drag"></a>Drag</h3>
                  <p>对于 Drag 组件来说，它是一个被拖动的对象，我们需要将这个 Drag 滑块拖动到 Drop 滑块上，就代表拖动成功了。它有三个主要的时间需要监听：dragstart、drag、dragend，分别代表拖动开始、拖动中、拖动结束三个事件，我们这里也分别设置了三个回调方法 onDragStart、onDrag、onDragEnd。 对于 onDragStart 方法来说，应该怎么实现呢？这里应该处理刚拖动的一瞬间的动作，由于我们需要记录拖动的轨迹，所以声明一个 trace 全局变量来保存轨迹信息，onDragStart 要做的就是初始化 trace 对象为空，另外记录一下初始的拖动位置，以便后续计算拖动路径，所以可以实现如下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="title">onDragStart</span><span class="params">(data, event)</span></span> &#123;</span><br><span class="line">    this<span class="selector-class">.init</span> = &#123;</span><br><span class="line">        x: event<span class="selector-class">.offsetX</span>,</span><br><span class="line">        y: event<span class="selector-class">.offsetY</span>,</span><br><span class="line">    &#125;</span><br><span class="line">    this<span class="selector-class">.trace</span> = []</span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>对于 onDrag 方法来说，就是处理拖动过程中的一系列拖动动作，这里其实就是计算当前拖动的偏移位置，然后把它保存到 trace 变量里面，所以可以实现如下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="title">onDrag</span><span class="params">(data, event)</span></span> &#123;</span><br><span class="line">    let offsetX = event<span class="selector-class">.offsetX</span> - this<span class="selector-class">.init</span>.x</span><br><span class="line">    let offsetY = event<span class="selector-class">.offsetY</span> - this<span class="selector-class">.init</span>.y</span><br><span class="line">    this<span class="selector-class">.trace</span>.push(&#123;</span><br><span class="line">        x: offsetX,</span><br><span class="line">        y: offsetY,</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>对于 onDragEnd 方法来说，其实就是检测最后的结果了，刚才我们用 state 变量里面的 over 属性来代表是否拖动到目标位置上，这里我们也定义了另外的 dragged 属性来代表是否已经拖动完成，dragging 属性来代表是否正在拖动，所以整个方法的逻辑上是检测 over 属性，然后对 dragging、dragged 属性做赋值，然后做一些相应的提示，实现如下：</p>
                  <figure class="highlight pf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">on</span>DragEnd() &#123;</span><br><span class="line">  if (this.<span class="keyword">state</span>.over) &#123;</span><br><span class="line">    this.<span class="keyword">state</span>.dragging = false</span><br><span class="line">    this.<span class="keyword">state</span>.dragged = true</span><br><span class="line">    this.<span class="variable">$message</span>.success('拖动成功')</span><br><span class="line">  &#125;</span><br><span class="line">  else &#123;</span><br><span class="line">    this.<span class="keyword">state</span>.dragging = false</span><br><span class="line">    this.<span class="keyword">state</span>.dragged = false</span><br><span class="line">    this.<span class="variable">$message</span>.error('拖动失败')</span><br><span class="line">  &#125;</span><br><span class="line">  this.<span class="keyword">state</span>.over = false</span><br><span class="line"> &#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>OK 了，以上便是主要的逻辑实现，这样我们就可以完成拖动滑块的定义以及拖动的监听了。 接下来就是一些样式上的问题了，对于图片的呈现，这里直接使用 CSS 的 background-image 样式来设置的，如果想显示图片的某一个范围，那就用 background-position 来设置，这是几个核心的要点。 好，这里的样式设置其实也可以用 JavaScript 来实现，我们把它们定义为一些计算属性：</p>
                  <figure class="highlight kotlin">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">wrapperStyle() &#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    width: <span class="keyword">this</span>.size.width + <span class="string">'px'</span>,</span><br><span class="line">    height: <span class="keyword">this</span>.size.height + <span class="string">'px'</span>,</span><br><span class="line">    backgroundImage: <span class="string">'url('</span> + <span class="keyword">this</span>.image + <span class="string">')'</span>,</span><br><span class="line">    backgroundSize: <span class="string">'cover'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;,</span><br><span class="line">targetStyle() &#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    left: <span class="keyword">this</span>.block.x + <span class="string">'px'</span>,</span><br><span class="line">    top: <span class="keyword">this</span>.block.y + <span class="string">'px'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;,</span><br><span class="line">sourceStyle() &#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    backgroundImage: <span class="string">'url('</span> + <span class="keyword">this</span>.image + <span class="string">')'</span>,</span><br><span class="line">    backgroundSize: <span class="keyword">this</span>.size.width + <span class="string">'px '</span> + <span class="keyword">this</span>.size.height + <span class="string">'px'</span>,</span><br><span class="line">    backgroundPosition: -<span class="keyword">this</span>.block.x + <span class="string">'px '</span> + -<span class="keyword">this</span>.block.y + <span class="string">'px'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>另外这里还有一个值得注意的地方，就是 Drag 组件的 slot 部分：</p>
                  <figure class="highlight elixir">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&lt;div slot=<span class="string">"image"</span> id=<span class="string">"float"</span> <span class="symbol">:style=<span class="string">"sourceStyle"</span>&gt;&lt;/div&gt;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这部分定义了在拖动过程中随鼠标移动的图片样式，这里也和 Drag 滑块一样定义了一样的样式，这样在拖动的过程中，就会显示一个和 Drag 滑块一样的滑块随鼠标移动。 最后，就是拖拽完成之后，将滑动轨迹输出出来，这里我就直接呈现在页面上了，<code>&lt;template&gt;</code> 区域加入如下定义即可：</p>
                  <figure class="highlight django">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;<span class="name">p</span> <span class="attr">v-if</span>=<span class="string">"state.dragged"</span> <span class="attr">id</span>=<span class="string">"trace"</span>&gt;</span></span></span><br><span class="line"><span class="xml">    拖动轨迹：</span><span class="template-variable">&#123;&#123; trace &#125;&#125;</span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>好，以上就是一些核心代码的介绍，还有一些细节的问题可以完善下，比如滑块随机初始化位置，以及拖动样式设置。 最后再看一遍效果： <img src="https://qiniu.cuiqingcai.com/2019-06-26-061556.gif" alt="拖动验证码示例"> 可以看到我们首先拖动了 Drag 滑块，当 Drag 滑块拖动到 Drop 滑块上时，出现了白色描边，证明已经拖动到目标位置了。然后松手之后，触发 onDragEnd 方法，呈现拖动轨迹，整个验证码就验证成功了。 当然这只是前端部分，如果在这个基础上添加表单验证，然后再添加后端校验，并加上轨迹识别，那可谓是一个完整的验证码系统了，在这里就点到为止啦。 最后如果大家想也仿照实现一下的话，可以参考这个组件：<a href="https://github.com/cameronhimself/vue-drag-drop" target="_blank" rel="noopener">https://github.com/cameronhimself/vue-drag-drop</a>，里面也介绍了其他的一些属性和事件，在某些情况下还是很有用的。 最后如果想获取本节代码，可以在「进击的Coder」公众号回复「验证码」获取。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-06-26 22:52:59" itemprop="dateCreated datePublished" datetime="2019-06-26T22:52:59+08:00">2019-06-26</time>
                </span>
                <span id="/6730.html" class="post-meta-item leancloud_visitors" data-flag-title="滑动验证码的原理并利用 Vue 实现滑动验证码" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>5.2k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>5 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6721.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6721.html" class="post-title-link" itemprop="url">Python 深度学习脚手架 ModelZoo</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>想必大家都或多或少听过 TensorFlow 的大名，这是 Google 开源的一个深度学习框架，里面的模型和 API 可以说基本是一应俱全，但 TensorFlow 其实有很多让人吐槽的地方，比如 TensorFlow 早期是只支持静态图的，你要调试和查看变量的值的话就得一个个变量运行查看它的结果，这是极其不友好的，而 PyTorch、Chainer 等框架就天生支持动态图，可以直接进行调试输出，非常方便。另外 TensorFlow 的 API 各个版本之间经常会出现不兼容的情况，比如 1.4 升到 1.7，里面有相当一部分 API 都被改了，里面有的是 API 名，有的直接改参数名，有的还给你改参数的顺序，如果要做版本兼容升级，非常痛苦。还有就是用 TensorFlow 写个模型，其实相对还是比较繁琐的，需要定义模型图，配置 Loss Function，配置 Optimizer，配置模型保存位置，配置 Tensor Summary 等等，其实并没有那么简洁。 然而为啥这么多人用 TensorFlow？因为它是 Google 家的，社区庞大，还有一个原因就是 API 你别看比较杂，但是确实比较全，contrib 模块里面你几乎能找到你想要的所有实现，而且更新确实快，一些前沿论文现在基本都已经在新版本里面实现了，所以，它的确是有它自己的优势。 然后再说说 Keras，这应该是除了 TensorFlow 之外，用的第二广泛的框架了，如果你用过 TensorFlow，再用上 Keras，你会发现用 Keras 搭模型实在是太方便了，而且如果你仔细研究下它的 API 设计，你会发现真的封装的非常科学，我感觉如果要搭建一个简易版的模型，Keras 起码得节省一半时间吧。 一个好消息是 TensorFlow 现在已经把 Keras 包进来了，也就是说如果你装了 TensorFlow，那就能同时拥有 TensorFlow 和 Keras 两个框架，哈哈，所以你最后还是装个 TensorFlow 就够了。 还有另一个好消息，刚才我不是吐槽了 TensorFlow 的静态图嘛？这的确是个麻烦的东西，不过现在的 TensorFlow 不一样了，它支持了 Eager 模式，也就是支持了动态图，有了它，我们可以就像写 Numpy 操作一样来搭建模型了，要看某个变量的值，很简单，直接 print 就 OK 了，不需要再去调用各种 run 方法了，可以直接抛弃 Session 这些繁琐的东西，所以基本上和 PyTorch 是一个套路的了，而且这个 Eager 模式在后续的 TensorFlow 2.0 版本将成为主打模式。简而言之，TensorFlow 比之前好用多了！ 好，以上说了这么多，我今天的要说的正题是什么呢？嗯，就是我基于 TensorFlow Eager 模式和 Keras 写了一个深度学习的框架。说框架也不能说框架，更准确地说应该叫脚手架，项目名字叫做 ModelZoo，中文名字可以理解成模型动物园。 有了这个脚手架，我们可以更加方便地实现一个深度学习模型，进一步提升模型开发的效率。 另外，既然是 ModelZoo，模型必不可少，我也打算以后把一些常用的模型来基于这个脚手架的架构实现出来，开源供大家使用。</p>
                  <h2 id="动机"><a href="#动机" class="headerlink" title="动机"></a>动机</h2>
                  <p>有人说，你这不是闲的蛋疼吗？人家 Keras 已经封装得很好了，你还写个啥子哦？嗯，没错，它的确是封装得很好了，但是我觉得某些地方是可以写得更精炼的。比如说，Keras 里面在模型训练的时候可以自定义 Callback，比如可以实现 Tensor Summary 的记录，可以保存 Checkpoint，可以配置 Early Stop 等等，但基本上，你写一个模型就要配一次吧，即使没几行代码，但这些很多情况都是需要配置的，所以何必每个项目都要再去写一次呢？所以，这时候就可以把一些公共的部分抽离出来，做成默认的配置，省去不必要的麻烦。 另外，我在使用过程中发现 Keras 的某些类并没有提供我想要的某些功能，所以很多情况下我需要重写某个功能，然后自己做封装，这其实也是一个可抽离出来的组件。 另外还有一个比较重要的一点就是，Keras 里面默认也不支持 Eager 模式，而 TensorFlow 新的版本恰恰又有了这一点，所以二者的兼并必然是一个绝佳的组合。 所以我写这个框架的目的是什么呢？</p>
                  <ul>
                    <li>第一，模型存在很多默认配置且可复用的地方，可以将默认的一些配置在框架中进行定义，这样我们只需要关注模型本身就好了。</li>
                    <li>第二，TensorFlow 的 Eager 模式便于 TensorFlow 的调试，Keras 的高层封装 API 便于快速搭建模型，取二者之精华。</li>
                    <li>第三，现在你可以看到要搜一个模型，会有各种花式的实现，有的用的这个框架，有的用的那个框架，而且参数、数据输入输出方式五花八门，实在是让人头大，定义这个脚手架可以稍微提供一些规范化的编写模式。</li>
                    <li>第四，框架名称叫做 ModelZoo，但我的理想也并不仅仅于实现一个简单的脚手架，我的愿景是把当前业界流行的模型都用这个框架实现出来，格式规范，API 统一，开源之后分享给所有人用，给他人提供便利。</li>
                  </ul>
                  <p>所以，ModelZoo 诞生了！</p>
                  <h2 id="开发过程"><a href="#开发过程" class="headerlink" title="开发过程"></a>开发过程</h2>
                  <p>开发的时候，我自己首先先实现了一些基本的模型，使用的是 TensorFlow Eager 和 Keras，然后试着抽离出来一些公共部分，将其封装成基础类，同时把模型独有的实现放开，供子类复写。然后在使用过程中自己还封装和改写过一些工具类，这部分也集成进来。另外就是把一些配置都规范化，将一些常用参数配置成默认参数，同时放开重写开关，在外部可以重定义。 秉承着上面的思想，我大约是在 10 月 6 日 那天完成了框架的搭建，然后在后续的几天基于这个框架实现了几个基础模型，最终打磨成了现在的样子。</p>
                  <h2 id="框架介绍"><a href="#框架介绍" class="headerlink" title="框架介绍"></a>框架介绍</h2>
                  <p>GitHub 地址：<a href="https://github.com/ModelZoo/ModelZoo" target="_blank" rel="noopener">https://github.com/ModelZoo/ModelZoo</a> 框架我已经发布到 PyPi，直接使用 pip 安装即可，目前支持 Python3，Python 2 尚未做测试，安装方式：</p>
                  <figure class="highlight sql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip3 <span class="keyword">install</span> <span class="keyword">model</span>-zoo</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>其实我是很震惊，这个名字居然没有被注册！GitHub 和 PyPi 都没有！不过现在已经被我注册了。 OK，接下来让我们看看用了它能怎样快速搭建一个模型吧！ 我们就以基本的线性回归模型为例来说明吧，这里有一组数据，是波士顿房价预测数据，输入是影响房价的各个因素，输出是房价本身，具体的数据集可以搜 Boston housing price regression dataset 了解一下。 总之，我们只需要知道这是一个回归模型就好了，输入 x 是一堆 Feature，输出 y 是一个数值，房价。好，那么我们就开始定义模型吧，模型的定义我们继承 ModelZoo 里面的 BaseModel 就好了，实现 model.py 如下：</p>
                  <figure class="highlight haskell">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="title">from</span> model_zoo.model <span class="keyword">import</span> BaseModel</span><br><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">BostonHousingModel</span>(<span class="type">BaseModel</span>):</span></span><br><span class="line"><span class="class">    def __init__(<span class="title">self</span>, <span class="title">config</span>):</span></span><br><span class="line"><span class="class">        super(<span class="type">BostonHousingModel</span>, <span class="title">self</span>).__init__(<span class="title">config</span>)</span></span><br><span class="line"><span class="class">        self.dense = tf.keras.layers.<span class="type">Dense</span>(1)</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class">    def call(<span class="title">self</span>, <span class="title">inputs</span>, <span class="title">training</span>=<span class="type">None</span>, <span class="title">mask</span>=<span class="type">None</span>):</span></span><br><span class="line"><span class="class">        o = self.dense(<span class="title">inputs</span>)</span></span><br><span class="line"><span class="class">        return o</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>好了，这就定义完了！有人会说，你的 Loss Function 呢？你的 Optimizer 呢？你的 Checkpoint 保存呢？你的 Tensor Summary 呢？不需要！因为我已经把这些配置封装到 BaseModel 了，有默认的 Loss Function、Optimizer、Checkpoint、Early Stop、Tensor Summary，这里只需要关注模型本身即可。 有人说，要是想自定义 Loss Function 咋办呢？自定义 Optimizer 咋办呢？很简单，只需要复写一些基本的配置或复写某个方法就好了。 如改写 Optimizer，只需要重写 optimizer 方法即可：</p>
                  <figure class="highlight ruby">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">optimizer</span><span class="params">(<span class="keyword">self</span>)</span></span><span class="symbol">:</span></span><br><span class="line">    <span class="keyword">return</span> tf.train.AdamOptimizer(<span class="number">0</span>.<span class="number">001</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>好，定义了模型之后怎么办？那当然是拿数据训练了，又要写数据加载，数据标准化，数据切分等等操作了吧，写到什么方法里？定义成什么样比较科学？现在，我们只需要实现一个 Trainer 就好了，然后复写 prepare_data 方法就好了，实现 train.py 如下：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> model_zoo.trainer <span class="keyword">import</span> BaseTrainer</span><br><span class="line"><span class="keyword">from</span> model_zoo.preprocess <span class="keyword">import</span> standardize</span><br><span class="line"></span><br><span class="line">tf.flags.DEFINE_integer(<span class="string">'epochs'</span>, <span class="number">100</span>, <span class="string">'Max epochs'</span>)</span><br><span class="line">tf.flags.DEFINE_string(<span class="string">'model_class'</span>, <span class="string">'BostonHousingModel'</span>, <span class="string">'Model class name'</span>)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Trainer</span><span class="params">(BaseTrainer)</span>:</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">prepare_data</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">from</span> tensorflow.python.keras.datasets <span class="keyword">import</span> boston_housing</span><br><span class="line">        (x_train, y_train), (x_eval, y_eval) = boston_housing.load_data()</span><br><span class="line">        x_train, x_eval = standardize(x_train, x_eval)</span><br><span class="line">        train_data, eval_data = (x_train, y_train), (x_eval, y_eval)</span><br><span class="line">        <span class="keyword">return</span> train_data, eval_data</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    Trainer().run()</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>好了，完事了，模型现在已经全部搭建完成！在这里只需要实现 prepare_data 方法，返回训练集和验证集即可，其他的什么都不需要！ 数据标准化在哪做的？这里我也封装好了方法。 运行在哪运行的？这里我也做好了封装。 模型保存在哪里做的？同样做好了封装。 Batch 切分怎么做的？这里也做好了封装。 我们只需要按照格式，返回这两组数据就好了，其他的什么都不用管！ 那同样的，模型保存位置，模型名称，Batch Size 多大，怎么设置？还是简单改下配置就好了。 如要修改模型保存位置，只需要复写一个 Flag 就好了：</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">tf.flags.<span class="constructor">DEFINE_string('<span class="params">checkpoint_dir</span>', '<span class="params">checkpoints</span>', <span class="params">help</span>='Data <span class="params">source</span> <span class="params">dir</span>')</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>好了，现在模型可以训练了！直接运行上面的代码就好了：</p>
                  <figure class="highlight vim">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">python3</span> train.<span class="keyword">py</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>结果是这样子的：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Epoch 1/100</span><br><span class="line"> 1/13 [=&gt;<span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span>] - ETA: 0s - loss: 816.1798</span><br><span class="line">13/13 [==============================] - 0s 4ms/<span class="keyword">step</span> - loss: 457.9925 - val_loss: 343.2489</span><br><span class="line"></span><br><span class="line">Epoch 2/100</span><br><span class="line"> 1/13 [=&gt;<span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span>] - ETA: 0s - loss: 361.5632</span><br><span class="line">13/13 [==============================] - 0s 3ms/<span class="keyword">step</span> - loss: 274.7090 - val_loss: 206.7015</span><br><span class="line">Epoch 00002: saving model <span class="keyword">to</span> checkpoints/model.ckpt</span><br><span class="line"></span><br><span class="line">Epoch 3/100</span><br><span class="line"> 1/13 [=&gt;<span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span>] - ETA: 0s - loss: 163.5308</span><br><span class="line">13/13 [==============================] - 0s 3ms/<span class="keyword">step</span> - loss: 172.4033 - val_loss: 128.0830</span><br><span class="line"></span><br><span class="line">Epoch 4/100</span><br><span class="line"> 1/13 [=&gt;<span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span>] - ETA: 0s - loss: 115.4743</span><br><span class="line">13/13 [==============================] - 0s 3ms/<span class="keyword">step</span> - loss: 112.6434 - val_loss: 85.0848</span><br><span class="line">Epoch 00004: saving model <span class="keyword">to</span> checkpoints/model.ckpt</span><br><span class="line"></span><br><span class="line">Epoch 5/100</span><br><span class="line"> 1/13 [=&gt;<span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span>] - ETA: 0s - loss: 149.8252</span><br><span class="line">13/13 [==============================] - 0s 3ms/<span class="keyword">step</span> - loss: 77.0281 - val_loss: 57.9716</span><br><span class="line"><span class="built_in">..</span><span class="built_in">..</span></span><br><span class="line"></span><br><span class="line">Epoch 42/100</span><br><span class="line"> 7/13 [===============&gt;<span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span><span class="built_in">..</span>] - ETA: 0s - loss: 20.5911</span><br><span class="line">13/13 [==============================] - 0s 8ms/<span class="keyword">step</span> - loss: 22.4666 - val_loss: 23.7161</span><br><span class="line">Epoch 00042: saving model <span class="keyword">to</span> checkpoints/model.ckpt</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到模型每次运行都会实时输出训练集和验证集的 Loss 的变化，另外还会自动保存模型，自动进行 Early Stop，自动保存 Tensor Summary。 可以看到这里运行了 42 个 Epoch 就完了，为什么？因为 Early Stop 的存在，当验证集经过了一定的 Epoch 一直不见下降，就直接停了，继续训练下去也没什么意义了。Early Stop 哪里配置的？框架也封装好了。 然后我们还可以看到当前目录下还生成了 events 和 checkpoints 文件夹，这一个是 TensorFlow Summary，供 TensorBoard 看的，另一个是保存的模型文件。 现在可以打开 TensorBoard 看看有什么情况，运行命令：</p>
                  <figure class="highlight ebnf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attribute">cd events</span></span><br><span class="line"><span class="attribute">tensorboard --logdir</span>=.</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p><img src="https://qiniu.cuiqingcai.com/2019-06-25-115250.jpg" alt=""> 可以看到训练和验证的 Loss 都被记录下来，并化成了图表展示。而这些东西我们配置过吗？没有，因为框架封装好了。 好，现在模型有了，我们要拿来做预测咋做呢？又得构建一边图，又得重新加载模型，又得准备数据，又得切分数据等等，还是麻烦，并没有，这里只需要这么定义就好了，定义 infer.py 如下：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> model_zoo.inferer <span class="keyword">import</span> BaseInferer</span><br><span class="line"><span class="keyword">from</span> model_zoo.preprocess <span class="keyword">import</span> standardize</span><br><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line">tf.flags.DEFINE_string(<span class="string">'checkpoint_name'</span>, <span class="string">'model.ckpt-20'</span>, help=<span class="string">'Model name'</span>)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Inferer</span><span class="params">(BaseInferer)</span>:</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">prepare_data</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">from</span> tensorflow.python.keras.datasets <span class="keyword">import</span> boston_housing</span><br><span class="line">        (x_train, y_train), (x_test, y_test) = boston_housing.load_data()</span><br><span class="line">        _, x_test = standardize(x_train, x_test)</span><br><span class="line">        <span class="keyword">return</span> x_test</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    result = Inferer().run()</span><br><span class="line">    print(result)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里只需要继承 BaseInferer，实现 prepare_data 方法就好了，返回的就是 test 数据集的 x 部分，其他的还是什么都不用干！ 另外这里额外定义了一个 Flag，就是 checkpoint_name，这个是必不可少的，毕竟要用哪个 Checkpoint 需要指定一下。 这里我们还是那数据集中的数据当测试数据，来看下它的输出结果：</p>
                  <figure class="highlight json">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[[ <span class="number">9.637125</span> ]</span><br><span class="line"> [<span class="number">21.368305</span> ]</span><br><span class="line"> [<span class="number">20.898445</span> ]</span><br><span class="line"> [<span class="number">33.832504</span> ]</span><br><span class="line"> [<span class="number">25.756516</span> ]</span><br><span class="line"> [<span class="number">21.264557</span> ]</span><br><span class="line"> [<span class="number">29.069794</span> ]</span><br><span class="line"> [<span class="number">24.968184</span> ]</span><br><span class="line"> ...</span><br><span class="line"> [<span class="number">36.027283</span> ]</span><br><span class="line"> [<span class="number">39.06852</span>  ]</span><br><span class="line"> [<span class="number">25.728745</span> ]</span><br><span class="line"> [<span class="number">41.62165</span>  ]</span><br><span class="line"> [<span class="number">34.340042</span> ]</span><br><span class="line"> [<span class="number">24.821484</span> ]]</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>就这样，预测房价结果就计算出来了，这个和输入的 x 内容都是一一对应的。 那有人又说了，我如果想拿到模型中的某个变量结果怎么办？还是很简单，因为有了 Eager 模式，直接输出就好。我要自定义预测函数怎么办？也很简单，复写 infer 方法就好了。 好，到现在为止，我们通过几十行代码就完成了这些内容：</p>
                  <ul>
                    <li>数据加载和预处理</li>
                    <li>模型图的搭建</li>
                    <li>Optimizer 的配置</li>
                    <li>运行结果的保存</li>
                    <li>Early Stop 的配置</li>
                    <li>Checkpoint 的保存</li>
                    <li>Summary 的生成</li>
                    <li>预测流程的实现</li>
                  </ul>
                  <p>总而言之，用了这个框架可以省去很多不必要的麻烦，同时相对来说比较规范，另外灵活可扩展。 以上就是 ModelZoo 的一些简单介绍。</p>
                  <h2 id="愿景"><a href="#愿景" class="headerlink" title="愿景"></a>愿景</h2>
                  <p>现在这个框架刚开发出来几天，肯定存在很多不成熟的地方，另外文档也还没有来得及写，不过我肯定是准备长期优化和维护下去的。另外既然取名叫做 ModelZoo，我后面也会把一些常用的深度学习模型基于该框架实现出来并发布，包括 NLP、CV 等各大领域，同时在实现过程中，也会发现框架本身的一些问题，并不断迭代优化。 比如基于该框架实现的人脸情绪识别的项目：<a href="https://github.com/ModelZoo/EmotionRecognition" target="_blank" rel="noopener">https://github.com/ModelZoo/EmotionRecognition</a> 其识别准确率还是可以的，比如输入这些图片： <img src="https://qiniu.cuiqingcai.com/2019-06-25-115311.jpg" alt=""> 模型便可以输出对应的情绪类型和情绪分布：</p>
                  <figure class="highlight yaml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">Image Path:</span> <span class="string">test1.png</span></span><br><span class="line"><span class="attr">Predict Result:</span> <span class="string">Happy</span></span><br><span class="line"><span class="attr">Emotion Distribution:</span> <span class="string">&#123;'Angry':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Disgust':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Fear':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Happy':</span> <span class="number">1.0</span><span class="string">,</span> <span class="attr">'Sad':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Surprise':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Neutral':</span> <span class="number">0.0</span><span class="string">&#125;</span></span><br><span class="line"><span class="string">====================</span></span><br><span class="line"><span class="attr">Image Path:</span> <span class="string">test2.png</span></span><br><span class="line"><span class="attr">Predict Result:</span> <span class="string">Happy</span></span><br><span class="line"><span class="attr">Emotion Distribution:</span> <span class="string">&#123;'Angry':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Disgust':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Fear':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Happy':</span> <span class="number">0.998</span><span class="string">,</span> <span class="attr">'Sad':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Surprise':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Neutral':</span> <span class="number">0.002</span><span class="string">&#125;</span></span><br><span class="line"><span class="string">====================</span></span><br><span class="line"><span class="attr">Image Path:</span> <span class="string">test3.png</span></span><br><span class="line"><span class="attr">Predict Result:</span> <span class="string">Surprise</span></span><br><span class="line"><span class="attr">Emotion Distribution:</span> <span class="string">&#123;'Angry':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Disgust':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Fear':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Happy':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Sad':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Surprise':</span> <span class="number">1.0</span><span class="string">,</span> <span class="attr">'Neutral':</span> <span class="number">0.0</span><span class="string">&#125;</span></span><br><span class="line"><span class="string">====================</span></span><br><span class="line"><span class="attr">Image Path:</span> <span class="string">test4.png</span></span><br><span class="line"><span class="attr">Predict Result:</span> <span class="string">Angry</span></span><br><span class="line"><span class="attr">Emotion Distribution:</span> <span class="string">&#123;'Angry':</span> <span class="number">1.0</span><span class="string">,</span> <span class="attr">'Disgust':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Fear':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Happy':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Sad':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Surprise':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Neutral':</span> <span class="number">0.0</span><span class="string">&#125;</span></span><br><span class="line"><span class="string">====================</span></span><br><span class="line"><span class="attr">Image Path:</span> <span class="string">test5.png</span></span><br><span class="line"><span class="attr">Predict Result:</span> <span class="string">Fear</span></span><br><span class="line"><span class="attr">Emotion Distribution:</span> <span class="string">&#123;'Angry':</span> <span class="number">0.04</span><span class="string">,</span> <span class="attr">'Disgust':</span> <span class="number">0.002</span><span class="string">,</span> <span class="attr">'Fear':</span> <span class="number">0.544</span><span class="string">,</span> <span class="attr">'Happy':</span> <span class="number">0.03</span><span class="string">,</span> <span class="attr">'Sad':</span> <span class="number">0.036</span><span class="string">,</span> <span class="attr">'Surprise':</span> <span class="number">0.31</span><span class="string">,</span> <span class="attr">'Neutral':</span> <span class="number">0.039</span><span class="string">&#125;</span></span><br><span class="line"><span class="string">====================</span></span><br><span class="line"><span class="attr">Image Path:</span> <span class="string">test6.png</span></span><br><span class="line"><span class="attr">Predict Result:</span> <span class="string">Sad</span></span><br><span class="line"><span class="attr">Emotion Distribution:</span> <span class="string">&#123;'Angry':</span> <span class="number">0.005</span><span class="string">,</span> <span class="attr">'Disgust':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Fear':</span> <span class="number">0.027</span><span class="string">,</span> <span class="attr">'Happy':</span> <span class="number">0.002</span><span class="string">,</span> <span class="attr">'Sad':</span> <span class="number">0.956</span><span class="string">,</span> <span class="attr">'Surprise':</span> <span class="number">0.0</span><span class="string">,</span> <span class="attr">'Neutral':</span> <span class="number">0.009</span><span class="string">&#125;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果大家对这个框架感兴趣，或者也想加入实现一些有趣的模型的话，可以在框架主页提 Issue 留言，我非常欢迎你的加入！另外如果大家感觉框架有不足的地方，也非常欢迎提 Issue 或发 PR，非常非常感谢！ 最后，如果你喜欢的话，还望能赠予它一个 Star，这样我也更有动力去维护下去。 项目的 GitHub 地址：<a href="https://github.com/ModelZoo/ModelZoo" target="_blank" rel="noopener">https://github.com/ModelZoo/ModelZoo</a>。 谢谢！</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-06-25 20:00:54" itemprop="dateCreated datePublished" datetime="2019-06-25T20:00:54+08:00">2019-06-25</time>
                </span>
                <span id="/6721.html" class="post-meta-item leancloud_visitors" data-flag-title="Python 深度学习脚手架 ModelZoo" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>8.6k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>8 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6485.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6485.html" class="post-title-link" itemprop="url">Python 机器学习算法二之逻辑回归的推导及实战</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>在线性回归模型中，我们实际上是建立了一个模型来拟合自变量和因变量之间的线性关系，但是在某些时候，我们要做的可能是一个分类模型，那么这里就可能用到线性回归模型的变种——逻辑回归，本节我们就逻辑回归来做一个详细的说明。</p>
                  <h2 id="实例引入"><a href="#实例引入" class="headerlink" title="实例引入"></a>实例引入</h2>
                  <p>我们还是以上一节的例子为例，张三、李四、王五、赵六都要贷款了，贷款时银行调查了他们的月薪和住房面积等因素，两个因素决定了贷款款项是否可以立即到账，下面列出来了他们四个人的工资情况、住房面积和到账的具体情况：</p>
                  <p>姓名</p>
                  <p>工资(元)</p>
                  <p>房屋面积(平方)</p>
                  <p>是否可立即到账</p>
                  <p>张三</p>
                  <p>6000</p>
                  <p>58</p>
                  <p>否</p>
                  <p>李四</p>
                  <p>9000</p>
                  <p>77</p>
                  <p>否</p>
                  <p>王五</p>
                  <p>11000</p>
                  <p>89</p>
                  <p>是</p>
                  <p>赵六</p>
                  <p>15000</p>
                  <p>54</p>
                  <p>是</p>
                  <p>看到了这样的数据，又来了一位孙七，他工资是 12000 元，房屋面积是 60 平，那的贷款可以立即到账吗？</p>
                  <h2 id="思路探索"><a href="#思路探索" class="headerlink" title="思路探索"></a>思路探索</h2>
                  <p>在这个例子中，我们不再是预测贷款金额具体是多少了，而是要对款项是否可以立即到账做一个分类，分类的结果要么是“否”要么是“是”，所以输出结果我们需要将其映射到一个区间内，让 0 代表“否”，1 代表“是”，这里我们就需要用一个函数来帮助我们实现这个功能，它的名字叫做 Sigmoid 函数，它的表达式是这样的： <script type="math/tex">g(z) = \\dfrac{1}{1+e^{-z}}</script> 它的函数图像是这样的： <img src="https://qiniu.cuiqingcai.com/2019-05-07-171241.jpg" alt=""> 它的定义域是全体实数，值域是 (0, 1)，当自变量小于 -5 的时候，其值就趋近于 0，当自变量大于 5 的时候，其值就趋近于 1，当自变量为 0 的时候，其值就等于 1/2。 所以我们如果在线性回归模型的基础上加一层 Sigmoid 函数，结果不就可以被转化为 0 到 1 了吗？这就很自然而然地转化为了一个分类模型。 我们知道，线性回归模型的表达形式是这样的： <script type="math/tex">h_{\\theta}(x) = \\sum_{i=0}^{n}\\theta_ix_i = \\theta^Tx</script> 如果我们在它的基础上加一层 Sigmoid 函数，其表达式就变成了： <script type="math/tex">h_{\\theta}(x) = g(\\theta^Tx) = \\dfrac{1}{1+e^{-\\theta^Tx}}</script> 好，现在我们来考虑下这个表达式表达的什么意思。 我们举例来说吧，如果这个函数的输出结果为 1，那么我们肯定认为结果为“是”。如果输出结果为 0 呢？我们就当然认为结果为 “否”了，但如果输出结果为 0.5 呢？我们只能模棱两可，此时我们既可以判断为“是”，也可以判断为“否”，但恰好为 0.5 的概率太小了，多多少少也有点偏差吧。所以如果输出结果为 0.51，那么我们怎么认为？我们认为“是”的概率会更大，否的概率更小，概率是多少？0.49。如果输出结果是 0.95 呢？“是”的概率是多少？不用多说，必然是 0.95。 因此，我们可以看出来，函数输出的结果就代表了预测为“是”的概率，也就是真实结果为 1 的概率。我们用公式表达下，这里我们用 $ h<em>{theta}(x) $ 表示预测结果，因此对于输入 x，分类类别为 1 和 0 的概率分别为： $$ \begin{cases} P(y = 1|x;\theta) = h</em>{\theta}(x) \\\\ P(y = 0|x;\theta) = 1 - h<em>{\theta}(x) \end{cases} <script type="math/tex">还是像线性回归一样，我们需要定义一个代价函数，让模型来优化这个代价函数。 在线性回归中，我们的代价函数是这样的：</script> J(\theta) = \dfrac{1}{2m}\sum</em>{i=1}^{m}(h<em>\theta(x^{(i)}) - y^{(i)})^2 <script type="math/tex">为什么是这样呢？是因为模型的输出结果和真实结果有一定的偏差，我们用它们的距离差的平方来定义了代价函数。 但是逻辑回归不一样啊，它又不是预测一个具体的数值，而是预测一个分类的概率，所以这里的代价函数我们可以写成这样子：</script> Cost(h</em>\theta(x), y) = \begin{cases} -log(h<em>\theta(x)), y = 1 \\\\ -log(1- h</em>\theta(x)), y = 0 \end{cases} <script type="math/tex">这里我们用了对数函数来定义它的损失值。比如当真实值 y 等于 1 的时候，如果我们的函数预测为 1，即 $ h_theta(x) $ 趋近于 1，这就代表预测对了，取对数再取反，结果趋近于 0，代表损失非常小。假如此时我们的函数预测结果趋近于 0，这就代表预测错了，取对数再取反，结果趋近于正无穷，代表损失特别大。所以，这个代价函数使用对数对预测错的结果进行了惩罚，偏离越大，其损失越大。 但是这个代价函数不好表示啊，我们想要把它写成一个表达式该怎么写？是这样的：</script> Cost(h<em>\theta(x), y) = -ylog(h</em>\theta(x)) - (1-y)log(1-h<em>\theta(x)) <script type="math/tex">这个式子和上面的式子是完全等价的。 好了，接下来我们计算所有样本的总的损失函数：</script> J(\theta) = \dfrac{1}{m}\sum</em>{i=1}^{m}Cost(h<em>\theta(x^{(i)}), y^{(i)})\\\\ = -\dfrac{1}{m}\sum</em>{i=1}^{m}[y^{(i)}log(h<em>\theta(x^{(i)})) + (1-y^{(i)})log(1-h</em>\theta(x^{(i)}))] $$ 和线性回归类似，这次我们的目的就是找到 $ theta $，使得 $ J(theta) $ 最小。</p>
                  <h2 id="推导过程"><a href="#推导过程" class="headerlink" title="推导过程"></a>推导过程</h2>
                  <p>这次我们就不能向上次一样用直接求偏导数，然后将偏导数置 0 的方法来求解 $ theta $ 了，这样是无法求解出具体的值的。 我们可以使用梯度下降法来进行求解，也就是一步步地求出 $theta$ 的更新过程，公式如下： <script type="math/tex">\\theta_j := \\theta_j - \\alpha\\dfrac{\\partial{J(\\theta)}}{\\partial(\\theta_j)}</script> 这里面最主要的就是求偏导，过程如下： <script type="math/tex">\\dfrac{\\partial{J(\\theta)}}{\\partial(\\theta_j)} \\\\\\\\ =-\\dfrac{1}{m}\\dfrac{\\partial{\\sum_{i=1}^{n}(y^{(i)}\\mathop{log}h_{\\theta}(x^{(i)}) + (1-y^{(i)})\\mathop{log}(1-h_{\\theta}(x^{(i)})))}}{\\partial(\\theta_j)} \\\\\\\\ =-\\dfrac{1}{m}\\sum_{i=1}^{n}(y^{(i)}\\dfrac{1}{h_{\\theta}(x^{(i)})}\\dfrac{\\partial{h_{\\theta}(x^{(i)})}}{\\partial{\\theta_j}} + (1-y^{(i)})\\dfrac{1}{1-h_{\\theta}(x^{(i)})}\\dfrac{\\partial{h_{\\theta}(x^{(i)})}}{\\partial{\\theta_j}}) \\\\\\\\ =-\\dfrac{1}{m}\\sum_{i=1}^{n}[y^{(i)}\\dfrac{1}{h_{\\theta}(x^{(i)})} + (1-y^{(i)})\\dfrac{1}{1-h_{\\theta}(x^{(i)})}] \\dfrac{\\partial{h_{\\theta}(x^{(i)})}}{\\partial{\\theta_j}} \\\\\\\\ =-\\dfrac{1}{m}\\sum_{i=1}^{n}[y^{(i)}\\dfrac{1}{h_{\\theta}(x^{(i)})} + (1-y^{(i)})\\dfrac{1}{1-h_{\\theta}(x^{(i)})}] h_{\\theta}(x^{(i)})(1-h_{\\theta}(x^{(i)}))\\dfrac{\\partial{\\theta^Tx^{(i)}}}{\\partial{\\theta_j}} \\\\\\\\ =-\\dfrac{1}{m}\\sum_{i=1}^{n}[y^{(i)}(1-h_{\\theta}(x^{(i)})) - (1-y^{(i)})h_{\\theta}(x^{(i)})]x_j^{(i)} \\\\\\\\ =-\\dfrac{1}{m}\\sum_{i=1}^{n}(y^{(i)} - h_{\\theta}(x^{(i)}))x_j^{(i)}</script> 因此： <script type="math/tex">\\theta_j := \\theta_j + \\dfrac{\\alpha}{m}\\sum_{i=1}^{n}(y^{(i)} - h_{\\theta}(x^{(i)}))x_j^{(i)}</script> 这时候我们发现，其梯度下降的推导结果和线性回归是一样的！</p>
                  <h2 id="编程实现"><a href="#编程实现" class="headerlink" title="编程实现"></a>编程实现</h2>
                  <p>下面我们还是用 Sklearn 中的 API 来实现逻辑回归模型，使用的库为 LogisticRegression，其 API 如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">class sklearn.linear_model.LogisticRegression(<span class="attribute">penalty</span>=’l2’, <span class="attribute">dual</span>=<span class="literal">False</span>, <span class="attribute">tol</span>=0.0001, <span class="attribute">C</span>=1.0, <span class="attribute">fit_intercept</span>=<span class="literal">True</span>, <span class="attribute">intercept_scaling</span>=1, <span class="attribute">class_weight</span>=None, <span class="attribute">random_state</span>=None, <span class="attribute">solver</span>=’liblinear’, <span class="attribute">max_iter</span>=100, <span class="attribute">multi_class</span>=’ovr’, <span class="attribute">verbose</span>=0, <span class="attribute">warm_start</span>=<span class="literal">False</span>, <span class="attribute">n_jobs</span>=1)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>参数说明如下：</p>
                  <ul>
                    <li>penalty：惩罚项，str 类型，可选参数为 l1 和 l2，默认为 l2。用于指定惩罚项中使用的规范。newton-cg、sag 和 lbfgs 求解算法只支持 L2 规范。L1G 规范假设的是模型的参数满足拉普拉斯分布，L2 假设的模型参数满足高斯分布，所谓的范式就是加上对参数的约束，使得模型更不会过拟合(overfit)，但是如果要说是不是加了约束就会好，这个没有人能回答，只能说，加约束的情况下，理论上应该可以获得泛化能力更强的结果。</li>
                    <li>dual：对偶或原始方法，bool 类型，默认为False。对偶方法只用在求解线性多核(liblinear)的 L2惩 罚项上。当样本数量 &gt; 样本特征的时候，dual 通常设置为 False。</li>
                    <li>tol：停止求解的标准，float类型，默认为1e-4。就是求解到多少的时候，停止，认为已经求出最优解。</li>
                    <li>c：正则化系数 λ 的倒数，float 类型，默认为 1.0。必须是正浮点型数。像 SVM 一样，越小的数值表示越强的正则化。</li>
                    <li>fit_intercept：是否存在截距或偏差，bool 类型，默认为 True。</li>
                    <li>intercept_scaling：仅在正则化项为”liblinear”，且fit_intercept 设置为 True 时有用。float 类型，默认为 1。</li>
                    <li>class_weight：用于标示分类模型中各种类型的权重，可以是一个字典或者’balanced’字符串，默认为不输入，也就是不考虑权重，即为 None。如果选择输入的话，可以选择 balanced 让类库自己计算类型权重，或者自己输入各个类型的权重。举个例子，比如对于0,1的二元模型，我们可以定义 class_weight={0:0.9,1:0.1}，这样类型0的权重为 90%，而类型 1 的权重为 10%。如果 class_weight选择 balanced，那么类库会根据训练样本量来计算权重。某种类型样本量越多，则权重越低，样本量越少，则权重越高。</li>
                    <li>random_state：随机数种子，int类型，可选参数，默认为无，仅在正则化优化算法为 sag，liblinear时有用。</li>
                    <li>solver：优化算法选择参数，只有五个可选参数，即newton-cg, lbfgs, liblinear, sag, saga。默认为liblinear。solver 参数决定了我们对逻辑回归损失函数的优化方法，有四种算法可以选择，分别是：<ul>
                        <li>liblinear：使用了开源的 liblinear 库实现，内部使用了坐标轴下降法来迭代优化损失函数。</li>
                        <li>lbfgs：拟牛顿法的一种，利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。</li>
                        <li>newton-cg：也是牛顿法家族的一种，利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。</li>
                        <li>sag：即随机平均梯度下降，是梯度下降法的变种，和普通梯度下降法的区别是每次迭代仅仅用一部分的样本来计算梯度，适合于样本数据多的时候。</li>
                        <li>saga：线性收敛的随机优化算法的的变重。</li>
                      </ul>
                    </li>
                    <li>max_iter：算法收敛最大迭代次数，int 类型，默认为10。仅在正则化优化算法为 newton-cg, sag 和 lbfgs 才有用，算法收敛的最大迭代次数。</li>
                    <li>multi_class：分类方式选择参数，str 类型，可选参数为 ovr 和 multinomial，默认为 ovr。ovr 即前面提到的one-vs-rest(OvR)，而 multinomial 即前面提到的 many-vs-many(MvM)。如果是二元逻辑回归，ovr 和 multinomial 并没有任何区别，区别主要在多元逻辑回归上。</li>
                    <li>verbose：日志冗长度，int 类型。默认为 0。就是不输出训练过程，1 的时候偶尔输出结果，大于 1，对于每个子模型都输出。</li>
                    <li>warm_start：热启动参数，bool 类型。默认为 False。如果为 True，则下一次训练是以追加树的形式进行（重新使用上一次的调用作为初始化）。</li>
                    <li>n_jobs：并行数。int 类型，默认为 1。1 的时候，用 CPU 的一个内核运行程序，2 的时候，用 CPU 的 2 个内核运行程序。为 -1 的时候，用所有 CPU 的内核运行程序。</li>
                  </ul>
                  <p>属性说明如下：</p>
                  <ul>
                    <li>coef_：斜率</li>
                    <li>intercept_：截距项</li>
                  </ul>
                  <p>我们现在来解决上面的示例，代码如下：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> sklearn.linear_model <span class="keyword">import</span> LogisticRegression</span><br><span class="line"></span><br><span class="line">x_data = [</span><br><span class="line">    [<span class="number">6000</span>, <span class="number">58</span>],</span><br><span class="line">    [<span class="number">9000</span>, <span class="number">77</span>],</span><br><span class="line">    [<span class="number">11000</span>, <span class="number">89</span>],</span><br><span class="line">    [<span class="number">15000</span>, <span class="number">54</span>]</span><br><span class="line">]</span><br><span class="line">y_data = [</span><br><span class="line">    <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">lr = LogisticRegression()</span><br><span class="line">lr.fit(x_data, y_data)</span><br><span class="line">x_test = [[<span class="number">12000</span>, <span class="number">60</span>]]</span><br><span class="line">print(<span class="string">'Intercept'</span>, lr.<span class="built_in">int</span>ercept_)</span><br><span class="line">print(<span class="string">'Coef'</span>, lr.coef_)</span><br><span class="line">print(<span class="string">'款项是否可以立即到账'</span>, lr.predict(x_test)[<span class="number">0</span>])</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们的 y_data 数据就变了，变成了是非判断，这里 0 代表“否”，1 代表“是”。这里做逻辑回归时使用了 LogisticRegression 对象，然后调用 fit() 方法进行拟合，拟合完毕之后，使用 [12000, 60] 这条数据作为输入，让模型输出它的预测值。 运行结果：</p>
                  <figure class="highlight lua">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Intercept [<span class="number">-0.03142387</span>]</span><br><span class="line">Coef <span class="string">[[ 0.00603919 -0.72587703]]</span></span><br><span class="line">款项是否可以立即到账 <span class="number">1</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>结果中输出了截距项和系数项，然后预测了是否可以立即到账，结果为 1，这表明孙七进行贷款，款项可以立即到账。 以上就是逻辑回归的基本推导和代码应用实现。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-06-21 10:54:19" itemprop="dateCreated datePublished" datetime="2019-06-21T10:54:19+08:00">2019-06-21</time>
                </span>
                <span id="/6485.html" class="post-meta-item leancloud_visitors" data-flag-title="Python 机器学习算法二之逻辑回归的推导及实战" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>6.1k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>6 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6645.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6645.html" class="post-title-link" itemprop="url">推荐给大家一个我整理的 Python 书单</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>六一八来了，现在各大平台都开始促销了，作为一名程序员，除了自己买一些大件和帮女朋友疯狂抢购，最好的选择就是买书好好学习技术了。 关注我的朋友可能很多都是学习 <strong>Python、爬虫、Web、数据分析、机器学习</strong>相关的。当然大家可能接触某个方向的时间不一样，可能有的同学已经对某个方向特别精通，有的同学在某个方向还处于入门阶段。 所以我花时间调研了一些大佬的推荐书单，同时结合我个人所看的一些书籍，另外参考了豆瓣、京东等排行榜和一些书评，选取了一些经典好评书籍和近期上市且反响不错的书籍，推荐给大家，内容涉及 <strong>Python 基础、**</strong>数据分析、机器学习、Web 开发<strong>等方向。 另外当当这边也有相当大力度的折扣，我也联系了当当获取了两个优惠码，大家可以用起来！这里也没有推销的意思，也没有用官方推荐的书单，是单纯根据书的内容来推荐的。 现在当当的书是</strong>每满 100 减 50<strong>，下面给大家两个优惠码，可以直接在满减的基础上</strong>继续折扣<strong>。 全品类优惠码： </strong>Y6NDPP<strong> 这个码买什么书都可以享受优惠，可享受</strong>满 200 减 40<strong> 优惠，叠加后相当于</strong>满 400 减 240<strong>，即 160 元可以购买 400 元的书籍，在</strong>当当 App 或小程序<strong>上下单有效。 计算机类： </strong>P6HFCE<strong> 这个码只能买计算机类的图书，可享受满 </strong>100 减 20<strong> 优惠，叠加后相当于</strong>满 200 减 120<strong>，在</strong>当当 App 或小程序上下单<strong>有效。 注意这两个优惠码</strong>有效期到 2019.06.20<strong>，过期就不能用了，而且</strong>数量是有限**的，多人共享，到了一定的使用次数就不能用了，后面我也没有办法补了，所以还是先到先得。 好，下面是我挑选的一些书籍，大家可以自行选购。也由于个人水平有限，很可能大家觉得优秀的书籍没有列出，如果大家有觉得不错的书籍，欢迎大家留言，大家也可以参考留言区的书籍来购买，谢谢大家支持。</p>
                  <h2 id="Python-编程"><a href="#Python-编程" class="headerlink" title="Python 编程"></a>Python 编程</h2>
                  <p>这一部分书籍是单纯讲解 Python 编程的，有入门的有进阶的，大家可以参考评分和出版时间选购。</p>
                  <h3 id="Python-入门"><a href="#Python-入门" class="headerlink" title="Python 入门"></a>Python 入门</h3>
                  <p>《Python编程：从入门到实践》/ 豆瓣 9.1 / 2016-7-1 出版 / [美] 埃里克·马瑟斯 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python编程：从入门到实践.jpg" alt=""> 《Python基础教程（第3版）》/ 豆瓣 8.0 / 2018-2-1 出版 / [挪] 海特兰德 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python基础教程（第3版）.jpg" alt=""> 《Python编程快速上手》/ 豆瓣 9.0 / 2016-7-1 出版 / [美] 思维加特 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python编程快速上手.jpg" alt=""> 《笨办法学Python3》/ 豆瓣 8.4 / 2018-6-1 出版 / [美] 泽德 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/笨办法学Python3.jpg" alt=""></p>
                  <h3 id="Python-进阶"><a href="#Python-进阶" class="headerlink" title="Python 进阶"></a>Python 进阶</h3>
                  <p>《流畅的Python》/ 豆瓣 9.4 / 2017-5-15 出版 / [巴西] 拉马略 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/流畅的Python.jpg" alt=""> 《Python Cookbook 中文版》/ 豆瓣 9.2 / 2015-5-1 出版 / [美] 比斯利 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python-Cookbook-中文版.jpg" alt=""> 《Effective Python》/ 豆瓣 9.0 / 2016-1-18 出版 / [美] 斯拉特金 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Effective-Python.jpg" alt=""> 《Python编程之美》/ 豆瓣 8.4 / 2018-8-1 出版 / [美] 肯尼思·赖茨 Kenneth Reitz <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python编程之美.jpg" alt=""> 《Python高性能编程》/ 豆瓣 7.2 / 2017-7-1 出版 / [美] 戈雷利克 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python高性能编程.jpg" alt=""></p>
                  <h2 id="数据及算法"><a href="#数据及算法" class="headerlink" title="数据及算法"></a>数据及算法</h2>
                  <h3 id="网络爬虫"><a href="#网络爬虫" class="headerlink" title="网络爬虫"></a>网络爬虫</h3>
                  <p>《Python网络爬虫权威指南（第2版）》/ 新书 / 2019-4-1 出版 / [美] 米切尔 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python网络爬虫权威指南（第2版）.jpg" alt=""> 《Python3网络爬虫开发实战》/ 豆瓣 9.0 / 2018-4-1 出版 / 崔庆才 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python3网络爬虫开发实战.jpg" alt=""> 数据分析 《利用Python进行数据分析（第2版）》/ 豆瓣 8.5 / 2018-7-30 出版 / [美] 麦金尼 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/利用Python进行数据分析（第2版）.jpg" alt=""> 《对比Excel，轻松学习Python数据分析》/ 豆瓣 7.8 / 2019-2-1 出版 / 张俊红 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/对比Excel，轻松学习Python数据分析.jpg" alt=""> 《Python数据分析与挖掘实战》/ 豆瓣 7.8 / 2016-1-1 出版 / 张良均 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python数据分析与挖掘实战.jpg" alt=""></p>
                  <h3 id="机器学习"><a href="#机器学习" class="headerlink" title="机器学习"></a>机器学习</h3>
                  <p>《统计学习方法（第2版）》/ 豆瓣 9.5 / 2018-5-5 出版 / 李航 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/统计学习方法（第2版）.jpg" alt=""> 《白话深度学习与TensorFlow》/ 豆瓣 7.1 / 2017-7-31 出版 / 高扬 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/白话深度学习与TensorFlow.jpg" alt=""> 《机器学习》/ 豆瓣 8.7 / 2016-1-1 出版 / 周志华 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/机器学习.jpg" alt=""> 《Python机器学习基础教程》/ 豆瓣 8.2 / 2018-1-1 出版 / [德] 穆勒 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python机器学习基础教程.jpg" alt=""> 《Python深度学习》/ 豆瓣 9.6 / 2018-8-1 出版 / [美] 肖莱 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python深度学习.jpg" alt=""> 《Python神经网络编程》/ 豆瓣 9.0 / 2018-4-1 出版 / [英] 拉希德 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Python神经网络编程.jpg" alt=""></p>
                  <h2 id="前后端技术"><a href="#前后端技术" class="headerlink" title="前后端技术"></a>前后端技术</h2>
                  <h3 id="前端"><a href="#前端" class="headerlink" title="前端"></a>前端</h3>
                  <p>《你不知道的JavaScript》/ 豆瓣 9.3 / 2018-1-1 出版 / [美] 辛普森 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/你不知道的JavaScript.jpg" alt=""> 《深入浅出Vue.js》/ 新书 / 2018-3-1 出版 / 刘博文 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/深入浅出Vue.js.jpg" alt=""></p>
                  <h3 id="后端"><a href="#后端" class="headerlink" title="后端"></a>后端</h3>
                  <p>《Django企业开发实战》/ 豆瓣 7.7 / 2019-2-1 出版 / 胡阳 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Django企业开发实战.jpg" alt=""> 《Flask Web开发（第2版）》/ 豆瓣 9.4 / 2018-8-1 出版 / [美] 格雷贝格 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Flask-Web开发（第2版）.jpg" alt=""> 《深入浅出Docker》/ 豆瓣 8.4 / 2019-4-1 出版 / [英] 波尔顿 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/深入浅出Docker.jpg" alt=""> 《深入理解Kafka》/ 新书 / 2019-1-1 出版 / 朱忠华 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/深入理解Kafka.jpg" alt=""> 《Kubernetes权威指南（第4版）》/ 新书 / 2018-5-1 出版 / 龚正 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/Kubernetes权威指南（第4版）.jpg" alt=""></p>
                  <h2 id="程序员经典"><a href="#程序员经典" class="headerlink" title="程序员经典"></a>程序员经典</h2>
                  <p>这是一些程序员通用的技能书，非常经典而且有用，几乎是必备书籍，强烈推荐。 《集体智慧编程》/ 豆瓣 9.0 / 2015-3-1 出版 / 西格兰 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/集体智慧编程.jpg" alt=""> 《程序员的数学》/ 豆瓣 8.6 / 2016-6-25 出版 / [日] 结城浩 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/程序员的数学.jpg" alt=""> 《程序员修炼之道》/ 豆瓣 8.6 / 2011-1-1 出版 / [美] 亨特 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/程序员修炼之道.jpg" alt=""> 《代码整洁之道》/ 豆瓣 8.6 / 2016-9-1 出版 / [美] 罗伯特 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/代码整洁之道.jpg" alt=""> 《持续交付2.0》/ 豆瓣 9.4 / 2019-1-1 出版 / 乔梁 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/持续交付2.0.jpg" alt=""> 《鸟哥的Linux私房菜（第4版）》/ 豆瓣 8.0 / 2018-11-1 出版 / 鸟哥 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/鸟哥的Linux私房菜（第4版）.jpg" alt=""> 《颈椎病康复指南》/ 豆瓣 8.6 / 2012-4-1 出版 / 陈选宁 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/颈椎病康复指南.jpg" alt=""> 以上便是我所推荐的书籍，数量有限，如果大家还有推荐的书籍，欢迎留言~</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-06-19 18:55:49" itemprop="dateCreated datePublished" datetime="2019-06-19T18:55:49+08:00">2019-06-19</time>
                </span>
                <span id="/6645.html" class="post-meta-item leancloud_visitors" data-flag-title="推荐给大家一个我整理的 Python 书单" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>2.2k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>2 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6613.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6613.html" class="post-title-link" itemprop="url">Python 机器学习算法一之线性回归的推导及实战</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>线性回归是机器学习中最基本的算法了，一般要学习机器学习都要从线性回归开始讲起，本节就对线性回归做一个详细的解释。</p>
                  <h2 id="实例引入"><a href="#实例引入" class="headerlink" title="实例引入"></a>实例引入</h2>
                  <p>在讲解线性回归之前，我们首先引入一个实例，张三、李四、王五、赵六都要贷款了，贷款时银行调查了他们的月薪和住房面积等因素，月薪越高，住房面积越大，可贷款金额越多，下面列出来了他们四个人的工资情况、住房面积和可贷款金额的具体情况：</p>
                  <p>姓名</p>
                  <p>工资(元)</p>
                  <p>房屋面积(平方)</p>
                  <p>可贷款金额(元)</p>
                  <p>张三</p>
                  <p>6000</p>
                  <p>58</p>
                  <p>30000</p>
                  <p>李四</p>
                  <p>9000</p>
                  <p>77</p>
                  <p>55010</p>
                  <p>王五</p>
                  <p>11000</p>
                  <p>89</p>
                  <p>73542</p>
                  <p>赵六</p>
                  <p>15000</p>
                  <p>54</p>
                  <p>63201</p>
                  <p>看到了这样的数据，又来了一位孙七，他工资是 12000 元，房屋面积是 60 平，那他大约能贷款多少呢？</p>
                  <h2 id="思路探索"><a href="#思路探索" class="headerlink" title="思路探索"></a>思路探索</h2>
                  <p>那这时候应该往哪方面考虑呢？如果我们假定可贷款金额和工资、房屋面积都是线性相关的，要解决这个问题，首先我们想到的应该就是初高中所学的一次函数吧，它的一般表达方式是 $ y = wx + b $，$ x $ 就是自变量，$ y $ 就是因变量，$ w $ 是自变量的系数，$ b $ 是偏移量，这个式子表明 $ y $ 和 $ x $ 是线性相关的，$ y $ 会随着 $ x $ 的变化而呈现线性变化。 现在回到我们的问题中，情况稍微不太一样，这个例子中是可贷款金额会随着工资和房屋面积而呈现线性变化，此时如果我们将工资定义为 $ x<em>1 $，房屋面积定义为 $ x_2 $，可贷款金额定义为 $ y $，那么它们三者的关系就可以表示为： $ y = w_1x_1 + w_2x_2 + b $，这里的自变量就不再是一个了，而是两个，分别是 $ x_1 $ 和 $ x_2 $，自变量系数就表示为了 $ w_1 $ 和 $ w_2 $，我们将其转化为表达的形式，同时将变量的名字换一下，就成了这个样子： $$ h</em>{\theta}(x) = \theta_0 + \theta_1x_1 + \theta_2x_2 $$ 这里只不过是将原表达式转为函数形式，换了个表示名字，另外参数名称从 $ w $ 换成了 $ \theta $，$ b $ 换成了 $ \theta_0 $，为什么要换？因为在机器学习算法中 $ \theta $ 用的更广泛一些，约定俗成。 然后这个问题怎么解？我们只需要求得一组近似的 $ \theta $ 参数使得我们的函数可以拟合已有的数据，然后整个函数表达式就可以表示出来了，然后再将孙七的工资和房屋面积代入进去，就求出来他可以贷款的金额了。</p>
                  <h2 id="思路拓展"><a href="#思路拓展" class="headerlink" title="思路拓展"></a>思路拓展</h2>
                  <p>那假如此时情景变一变，变得更复杂一些，可贷款金额不仅仅和工资、房屋面积有关，还有当前存款数、年龄等等因素有关，那我们的表达式应该怎么写？不用担心，我们有几个影响因素，就写定义几个变量，比如我们可以将存款数定义为 $ x<em>3 $，年龄定义为 $ x_4 $，如果还有其他影响因素我们可以继续接着定义，如果一共有 $ n $ 个影响因素，我们就定义到 $ x_n $，这时候函数表达式就可以变成这样子了： $$ h</em>{\theta}(x) = \theta<em>0 + \theta_1x_1 + \theta_2x_2 + … + \theta_nx_n <script type="math/tex">这个式子看起来挺长的不好写的吧，我们可以使用求和公式写成如下形式：</script> h</em>{\theta}(x) = \sum_{i=0}^{n}\theta_ix_i = \theta^Tx $$ 如果要使得这个公式成立，这里需要满足一个条件就是 $ x_0 = 1 $，其实在实际场景中 $ x_0 $ 是不存在的，因为第一个影响因素我们用 $ x_1 $ 来表示了，第二个影响因素我们用 $ x_2 $ 来表示了，依次类推。所以这里我们直接指定 $ x_0 = 1 $ 即可。 后来我们又将公式简化为线性代数的向量表示，这里 $ \theta^T $ 是 $ \theta $ 向量转置的结果，而 $ \theta $ 向量又表示为 $ (\theta_0, \theta_1, …, \theta_n) $，同样地，$ x $ 向量可以表示为 $ (x_0, x_1, …, x_n) $，总之，表达成后面的式子，看起来更简洁明了。 好了，这就是最基本的线性判别解析函数的写法，是不是很简单。</p>
                  <h2 id="实际求解"><a href="#实际求解" class="headerlink" title="实际求解"></a>实际求解</h2>
                  <p>那接下来我们怎样实际求解这个问题呢？比如拿张三的数据代入到这个函数表达式中，这里还是假设有两个影响因素，张三的数据我们可以表示为 $ x<em>1^{(1)} = 6000, x_2^{(1)} = 58, y^{(1)} = 30000 $，注意这里我们在数据的右上角加了一个小括号，里面带有数字，如果我们把张三的数据看成一个条目，那么这个数字就代表了这个条目的序号，1 就代表第一条数据，2 就代表第二条数据，为啥这么写？也是约定俗成，以后也会经常采用这样的写法，记住就好了。 所以，我们的愿景是要使得我们的函数能够拟合当前的这条数据，所以我们希望是这样的情况： $$ y^{(1)} = \sum</em>{i=0}^{n}\theta<em>{i}x</em>{i}^{(1)} = \theta^Tx^{(1)} <script type="math/tex">其中 $ y^{(1)} $ 是真实值，但我们知道，哪有那么容易十全十美，丝毫不差的拟合函数，所以上面的式子一般来说是不成立的，函数计算值和真实值还是多少还是有一定的误差的吧，如果我们想知道函数真实拟合的结果的话，我们需要把 $ x $ 变量代入函数，会得到一个函数本身拟合的结果，是这样的：</script> h<em>{\theta}(x^{(1)}) = \sum</em>{i=0}^{n}\theta<em>{i}x</em>{i}^{(1)} = \theta^Tx^{(1)} <script type="math/tex">这里的 $ h_{\\theta}(x^{(1)}) $ 就是我们函数得到的结果了。一般来说，二者是会存在一定的误差的，所以误差我们一般可以写成他们的差的绝对值或平方的形式，使用绝对值或平方的目的是消去正负号的影响，比如写成平方的形式就是这样子：</script> (h<em>{\theta}(x^{(1)}) - y^{(1)})^2 <script type="math/tex">这个式子就是我们函数真实拟合值和真实值之间的差距，没问题吧。 相应的，如果是李四的数据，误差就可以写为：</script> (h</em>{\theta}(x^{(2)}) - y^{(2)})^2 <script type="math/tex">依次类推，如果是第 $ i $ 条数据，误差就可以写成：</script> (h<em>{\theta}(x^{(i)}) - y^{(i)})^2 <script type="math/tex">要使得我们的函数对所有的数据都能尽量很好地拟合，我们可以把这些误差加起来求个平均，假设一共 $ m $ 条数据，那么所有数据的误差可以写成如下形式：</script> J(\theta) = \dfrac{1}{2m}\sum</em>{i=1}^{m}(h_\theta(x^{(i)}) - y^{(i)})^2 $$ 注意这里 $ i $ 指的是第几条数据，是从 1 开始的，一直到 $ m $ 为止，然后使用了求和公式对每一条数据的误差进行累加和，最后除以了 $ 2m $，我们的最终目的就是找出合适的 $ \theta $，使得这个 $ J(\theta ) $ 的值最小，即误差最小，在机器学习中，我们就把 $ J(\theta) $ 称为损失函数（Loss Function），即我们要使得损失值最小。 有的小伙伴可能好奇损失函数前面为什么是 $ 2m $，而不是 $ m $？因为我们后面要用到这个算式的导数，所以这里多了个 2 是为了便于求导计算。况且一个表达式要求最小值，前面乘一个常数是对结果没影响的。</p>
                  <h2 id="求解过程"><a href="#求解过程" class="headerlink" title="求解过程"></a>求解过程</h2>
                  <p>由于我们求解的是线性回归问题，所以整个损失函数的图像非常简单清晰，如果只有 $ \theta<em>1 $ 和 $ \theta_2 $ 两个参数，我们甚至可以直接画出其图像，整个损失函数大小随 $ \theta_1 $ 和 $ \theta_2 $ 的变化实际上类似于这样子： <img src="https://qiniu.cuiqingcai.com/2019-06-12-074355.jpg" alt=""> 可以看到这是一个凸函数，竖轴代表损失函数的大小，横纵两轴代表 $ \theta_1 $ 和 $ \theta_2 $ 的变化，可见在中间的最低谷损失函数取得最小值，这时候损失函数在 $ \theta_1 $ 和 $ \theta_2 $ 上的导数都是 0，因此我们可以一步到位，直接用偏导置零的方式来求解损失函数取得最小值时的 $ \theta $ 值的大小。 所以我们可以先对每个 $ \theta $ 求解其偏导结果，这里 $ \theta $ 表示为 $ \theta_j $，代表 $ \theta $ 中的某一维： $$ \dfrac{\partial{J(\theta)}}{\partial{\theta_j}} = \dfrac{1}{2m} \dfrac{\partial({\sum</em>{i=1}^{m}{(y^{(i)} - h<em>{\theta}(x^{(i)}))^2}})}{\partial{\theta_j}} \\\\ =\dfrac{1}{m}\sum</em>{i=1}^{m}((h<em>{\theta}(x^{(i)}) - y^{(i)})x_j^{(i)}) <script type="math/tex">直接将偏导置零即可直接求解 $ \\theta_j $： ​</script> \sum</em>{i=1}^{m} {h<em>{\theta}(x^{(i)})x_j^{(i)}} - \sum</em>{i=1}^{m}y^{(i)}x<em>j^{(i)} = 0 <script type="math/tex">这里将所有数据代入，即可通过求解方程组的形式直接解出 $ \\theta_j $ 的值，但这些方程组里面其实 $ \\theta_j $ 之间存在彼此依赖关系，需要联立求解出来。 如果不用这种求解方式，我们可以使用梯度下降的方式来进行求解，在这里 $ \\theta_j $ 只需要逐步更新即可：</script> \theta_j = \theta_j - \alpha\dfrac{\partial{J(\theta)}}{\partial{\theta_j}} \\\\ = \theta_j - \dfrac{\alpha}{m}\sum</em>{i=1}^{m}((h_{\theta}(x^{(i)}) - y^{(i)})x_j^{(i)}) $$ 这里 $ \alpha $ 就是学习率，$ \theta_j $ 每经过一步都会进行一次更新，得到新的结果，经过梯度下降过程，$ \theta_j $ 都会更新为使得梯度最小化的数值，最后就完成了 $ \theta $ 的求解。 以上便是线性回归的整个推导和求解过程。</p>
                  <h2 id="实战操作"><a href="#实战操作" class="headerlink" title="实战操作"></a>实战操作</h2>
                  <p>现在呢，我们想要根据前面的数据来求解这个真实的问题，为了解决这个问题，我们在这里用 Python 的 Sklearn 库来实现。 对于线性回归来说，Sklearn 已经做好了封装，直接使用 LinearRegression 即可。 它的 API 如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">class sklearn.linear_model.LinearRegression(<span class="attribute">fit_intercept</span>=<span class="literal">True</span>, <span class="attribute">normalize</span>=<span class="literal">False</span>, <span class="attribute">copy_X</span>=<span class="literal">True</span>, <span class="attribute">n_jobs</span>=None)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>参数解释如下：</p>
                  <ul>
                    <li>fit_intercept : 布尔值，是否使用偏置项，默认是 True。</li>
                    <li>normalize : 布尔值，是否启用归一化，默认是 False。当 fit_intercept 被置为 False 的时候，这个参数会被忽略。当该参数为 True 时，数据会被归一化处理。</li>
                    <li>copy_X : 布尔值，默认是 True，如果为 True，x 参数会被拷贝不会影响原来的值，否则会被复写。</li>
                    <li>n_jobs：数值或者布尔，如果设置了，则多核并行处理。</li>
                  </ul>
                  <p>属性如下：</p>
                  <ul>
                    <li>coef_：x 的权重系数大小</li>
                    <li>intercept_：偏置项大小</li>
                  </ul>
                  <p>代码实现如下：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> sklearn.linear_model <span class="keyword">import</span> LinearRegression</span><br><span class="line"></span><br><span class="line">x_data = [</span><br><span class="line">    [<span class="number">6000</span>, <span class="number">58</span>],</span><br><span class="line">    [<span class="number">9000</span>, <span class="number">77</span>],</span><br><span class="line">    [<span class="number">11000</span>, <span class="number">89</span>],</span><br><span class="line">    [<span class="number">15000</span>, <span class="number">54</span>]</span><br><span class="line">]</span><br><span class="line">y_data = [</span><br><span class="line">    <span class="number">30000</span>, <span class="number">55010</span>, <span class="number">73542</span>, <span class="number">63201</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">lr = LinearRegression()</span><br><span class="line">lr.fit(x_data, y_data)</span><br><span class="line">print(<span class="string">'方程为：y=&#123;w1&#125;x1+&#123;w2&#125;x2+&#123;b&#125;'</span>.format(w1=round(lr.coef_[<span class="number">0</span>], <span class="number">2</span>),</span><br><span class="line">                                       w2=round(lr.coef_[<span class="number">1</span>], <span class="number">2</span>),</span><br><span class="line">                                       b=lr.<span class="built_in">int</span>ercept_))</span><br><span class="line">x_test = [[<span class="number">12000</span>, <span class="number">60</span>]]</span><br><span class="line">print(<span class="string">'贷款金额为：'</span>, lr.predict(x_test)[<span class="number">0</span>])</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">方程为：y=<span class="number">4.06</span>x1+<span class="number">743.15</span>x2+<span class="number">-37831.862532707615</span></span><br><span class="line">贷款金额为：<span class="number">55484.33779181102</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>在这里我们首先声明了 LinearRegression 对象，然后将数据整合成 x<em>data 和 y_data 的形式，然后通过调用 fit() 方法来对数据进行拟合。 拟合完毕之后，LinearRegression 的 coef</em> 对象就是各个 x 变量的权重大小，即对应着 $ \theta<em>1, \theta_2 $，intercept</em> 则是偏移量，对应着 $ \theta_0 $，这样我们就可以得到一个线性回归表达式了。 然后我们再调用 predict() 方法，将新的测试数据传入，便可以得到其预测结果，最终结果为 55484.34，即孙七的可贷款额度为 55484.34 元。 以上便是机器学习中线性回归算法的推导解析和相关调用实现。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-06-19 11:30:36" itemprop="dateCreated datePublished" datetime="2019-06-19T11:30:36+08:00">2019-06-19</time>
                </span>
                <span id="/6613.html" class="post-meta-item leancloud_visitors" data-flag-title="Python 机器学习算法一之线性回归的推导及实战" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>5.1k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>5 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6552.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6552.html" class="post-title-link" itemprop="url">Python 使用 attrs 和 cattrs 实现面向对象编程</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>Python 是支持面向对象的，很多情况下使用面向对象编程会使得代码更加容易扩展，并且可维护性更高，但是如果你写的多了或者某一对象非常复杂了，其中的一些写法会相当相当繁琐，而且我们会经常碰到对象和 JSON 序列化及反序列化的问题，原生的 Python 转起来还是很费劲的。 可能这么说大家会觉得有点抽象，那么这里举几个例子来感受一下。 首先让我们定义一个对象吧，比如颜色。我们常用 RGB 三个原色来表示颜色，R、G、B 分别代表红、绿、蓝三个颜色的数值，范围是 0-255，也就是每个原色有 256 个取值。如 RGB(0, 0, 0) 就代表黑色，RGB(255, 255, 255) 就代表白色，RGB(255, 0, 0) 就代表红色，如果不太明白可以具体看看 RGB 颜色的定义哈。 好，那么我们现在如果想定义一个颜色对象，那么正常的写法就是这样了，创建这个对象的时候需要三个参数，就是 R、G、B 三个数值，定义如下：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Color</span><span class="params">(object)</span>:</span></span><br><span class="line">    <span class="string">"""</span></span><br><span class="line"><span class="string">    Color Object of RGB</span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, r, g, b)</span>:</span></span><br><span class="line">        self.r = r</span><br><span class="line">        self.g = g</span><br><span class="line">        self.b = b</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>其实对象一般就是这么定义的，初始化方法里面传入各个参数，然后定义全局变量并赋值这些值。其实挺多常用语言比如 Java、PHP 里面都是这么定义的。但其实这种写法是比较冗余的，比如 r、g、b 这三个变量一写就写了三遍。 好，那么我们初始化一下这个对象，然后打印输出下，看看什么结果：</p>
                  <figure class="highlight dos">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="built_in">color</span> = <span class="built_in">Color</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">color</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>结果是什么样的呢？或许我们也就能看懂一个 Color 吧，别的都没有什么有效信息，像这样子：</p>
                  <figure class="highlight dos">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&lt;__main__.<span class="built_in">Color</span> object <span class="built_in">at</span> <span class="number">0</span>x103436f60&gt;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>我们知道，在 Python 里面想要定义某个对象本身的打印输出结果的时候，需要实现它的 <code>__repr__</code> 方法，所以我们比如我们添加这么一个方法：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(self)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">f'<span class="subst">&#123;self.__class__.__name__&#125;</span>(r=<span class="subst">&#123;self.r&#125;</span>, g=<span class="subst">&#123;self.g&#125;</span>, b=<span class="subst">&#123;self.b&#125;</span>)'</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里使用了 Python 中的 fstring 来实现了 <code>__repr__</code> 方法，在这里我们构造了一个字符串并返回，字符串中包含了这个 Color 类中的 r、g、b 属性，这个返回的结果就是 print 的打印结果，我们再重新执行一下，结果就变成这样子了：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Color(<span class="attribute">r</span>=255, <span class="attribute">g</span>=255, <span class="attribute">b</span>=255)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>改完之后，这样打印的对象就会变成这样的字符串形式了，感觉看起来清楚多了吧？ 再继续，如果我们要想实现这个对象里面的 <code>__eq__</code>、<code>__lt__</code> 等各种方法来实现对象之间的比较呢？照样需要继续定义成类似这样子的形式：</p>
                  <figure class="highlight gml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">def __lt__(<span class="literal">self</span>, <span class="literal">other</span>):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> isinstance(<span class="literal">other</span>, <span class="literal">self</span>.__class__): <span class="keyword">return</span> NotImplemented</span><br><span class="line">    <span class="keyword">return</span> (<span class="literal">self</span>.r, <span class="literal">self</span>.g, <span class="literal">self</span>.b) &lt; (<span class="literal">other</span>.r, <span class="literal">other</span>.g, <span class="literal">other</span>.b)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里是 <code>__lt__</code> 方法，有了这个方法就可以使用比较符来对两个 Color 对象进行比较了，但这里又把这几个属性写了两遍。 最后再考虑考虑，如果我要把 JSON 转成 Color 对象，难道我要读完 JSON 然后一个个属性赋值吗？如果我想把 Color 对象转化为 JSON，又得把这几个属性写几遍呢？如果我突然又加了一个属性比如透明度 a 参数，那么整个类的方法和参数都要修改，这是极其难以扩展的。不知道你能不能忍，反正我不能忍！ 如果你用过 Scrapy、Django 等框架，你会发现 Scrapy 里面有一个 Item 的定义，只需要定义一些 Field 就可以了，Django 里面的 Model 也类似这样，只需要定义其中的几个字段属性就可以完成整个类的定义了，非常方便。 说到这里，我们能不能把 Scrapy 或 Django 里面的定义模式直接拿过来呢？能是能，但是没必要，因为我们还有专门为 Python 面向对象而专门诞生的库，没错，就是 attrs 和 cattrs 这两个库。 有了 attrs 库，我们就可以非常方便地定义各个对象了，另外对于 JSON 的转化，可以进一步借助 cattrs 这个库，非常有帮助。 说了这么多，还是没有介绍这两个库的具体用法，下面我们来详细介绍下。</p>
                  <h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2>
                  <p>安装这两个库非常简单，使用 pip 就好了，命令如下：</p>
                  <figure class="highlight cmake">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip3 <span class="keyword">install</span> attrs cattrs</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>安装好了之后我们就可以导入并使用这两个库了。</p>
                  <h2 id="简介与特性"><a href="#简介与特性" class="headerlink" title="简介与特性"></a>简介与特性</h2>
                  <p>首先我们来介绍下 attrs 这个库，其官方的介绍如下：</p>
                  <blockquote>
                    <p>attrs 是这样的一个 Python 工具包，它能将你从繁综复杂的实现上解脱出来，享受编写 Python 类的快乐。它的目标就是在不减慢你编程速度的前提下，帮助你来编写简洁而又正确的代码。</p>
                  </blockquote>
                  <p>其实意思就是用了它，定义和实现 Python 类变得更加简洁和高效。</p>
                  <h2 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h2>
                  <p>首先明确一点，我们现在是装了 attrs 和 cattrs 这两个库，但是实际导入的时候是使用 attr 和 cattr 这两个包，是不带 s 的。 在 attr 这个库里面有两个比较常用的组件叫做 attrs 和 attr，前者是主要用来修饰一个自定义类的，后者是定义类里面的一个字段的。有了它们，我们就可以将上文中的定义改写成下面的样子：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Color</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">r</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">g</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">b</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">color</span> = <span class="symbol">Color</span>(<span class="symbol">255, <span class="symbol">255</span>, <span class="symbol">255</span></span>)</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">color</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>看我们操作的，首先我们导入了刚才所说的两个组件，然后用 attrs 里面修饰了 Color 这个自定义类，然后用 attrib 来定义一个个属性，同时可以指定属性的类型和默认值。最后打印输出，结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Color(<span class="attribute">r</span>=255, <span class="attribute">g</span>=255, <span class="attribute">b</span>=255)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>怎么样，达成了一样的输出效果！ 观察一下有什么变化，是不是变得更简洁了？r、g、b 三个属性都只写了一次，同时还指定了各个字段的类型和默认值，另外也不需要再定义 <code>__init__</code> 方法和 <code>__repr__</code> 方法了，一切都显得那么简洁。一个字，爽！ 实际上，主要是 attrs 这个修饰符起了作用，然后根据定义的 attrib 属性自动帮我们实现了 <code>__init__</code>、<code>__repr__</code>、<code>__eq__</code>、<code>__ne__</code>、<code>__lt__</code>、<code>__le__</code>、<code>__gt__</code>、<code>__ge__</code>、<code>__hash__</code> 这几个方法。 如使用 attrs 修饰的类定义是这样子：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">SmartClass</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">a</span> = <span class="symbol">attrib</span>()</span><br><span class="line">    <span class="symbol">b</span> = <span class="symbol">attrib</span>()</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>其实就相当于已经实现了这些方法：</p>
                  <figure class="highlight ruby">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RoughClass</span>(<span class="title">object</span>):</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(<span class="keyword">self</span>, a, b)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">self</span>.a = a</span><br><span class="line">        <span class="keyword">self</span>.b = b</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(<span class="keyword">self</span>)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">"RoughClass(a=&#123;&#125;, b=&#123;&#125;)"</span>.format(<span class="keyword">self</span>.a, <span class="keyword">self</span>.b)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__eq__</span><span class="params">(<span class="keyword">self</span>, other)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">if</span> other.__class_<span class="number">_</span> is <span class="keyword">self</span>.<span class="symbol">__class__:</span></span><br><span class="line">            <span class="keyword">return</span> (<span class="keyword">self</span>.a, <span class="keyword">self</span>.b) == (other.a, other.b)</span><br><span class="line">        <span class="symbol">else:</span></span><br><span class="line">            <span class="keyword">return</span> NotImplemented</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__ne__</span><span class="params">(<span class="keyword">self</span>, other)</span></span><span class="symbol">:</span></span><br><span class="line">        result = <span class="keyword">self</span>.__eq_<span class="number">_</span>(other)</span><br><span class="line">        <span class="keyword">if</span> result is <span class="symbol">NotImplemented:</span></span><br><span class="line">            <span class="keyword">return</span> NotImplemented</span><br><span class="line">        <span class="symbol">else:</span></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">not</span> result</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__lt__</span><span class="params">(<span class="keyword">self</span>, other)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">if</span> other.__class_<span class="number">_</span> is <span class="keyword">self</span>.<span class="symbol">__class__:</span></span><br><span class="line">            <span class="keyword">return</span> (<span class="keyword">self</span>.a, <span class="keyword">self</span>.b) &lt; (other.a, other.b)</span><br><span class="line">        <span class="symbol">else:</span></span><br><span class="line">            <span class="keyword">return</span> NotImplemented</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__le__</span><span class="params">(<span class="keyword">self</span>, other)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">if</span> other.__class_<span class="number">_</span> is <span class="keyword">self</span>.<span class="symbol">__class__:</span></span><br><span class="line">            <span class="keyword">return</span> (<span class="keyword">self</span>.a, <span class="keyword">self</span>.b) &lt;= (other.a, other.b)</span><br><span class="line">        <span class="symbol">else:</span></span><br><span class="line">            <span class="keyword">return</span> NotImplemented</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__gt__</span><span class="params">(<span class="keyword">self</span>, other)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">if</span> other.__class_<span class="number">_</span> is <span class="keyword">self</span>.<span class="symbol">__class__:</span></span><br><span class="line">            <span class="keyword">return</span> (<span class="keyword">self</span>.a, <span class="keyword">self</span>.b) &gt; (other.a, other.b)</span><br><span class="line">        <span class="symbol">else:</span></span><br><span class="line">            <span class="keyword">return</span> NotImplemented</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__ge__</span><span class="params">(<span class="keyword">self</span>, other)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">if</span> other.__class_<span class="number">_</span> is <span class="keyword">self</span>.<span class="symbol">__class__:</span></span><br><span class="line">            <span class="keyword">return</span> (<span class="keyword">self</span>.a, <span class="keyword">self</span>.b) &gt;= (other.a, other.b)</span><br><span class="line">        <span class="symbol">else:</span></span><br><span class="line">            <span class="keyword">return</span> NotImplemented</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__hash__</span><span class="params">(<span class="keyword">self</span>)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">return</span> hash((<span class="keyword">self</span>.__class_<span class="number">_</span>, <span class="keyword">self</span>.a, <span class="keyword">self</span>.b))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>所以说，如果我们用了 attrs 的话，就可以不用再写这些冗余又复杂的代码了。 翻看源码可以发现，其内部新建了一个 ClassBuilder，通过一些属性操作来动态添加了上面的这些方法，如果想深入研究，建议可以看下 attrs 库的源码。</p>
                  <h2 id="别名使用"><a href="#别名使用" class="headerlink" title="别名使用"></a>别名使用</h2>
                  <p>这时候大家可能有个小小的疑问，感觉里面的定义好乱啊，库名叫做 attrs，包名叫做 attr，然后又导入了 attrs 和 attrib，这太奇怪了。为了帮大家解除疑虑，我们来梳理一下它们的名字。 首先库的名字就叫做 attrs，这个就是装 Python 包的时候这么装就行了。但是库的名字和导入的包的名字确实是不一样的，我们用的时候就导入 attr 这个包就行了，里面包含了各种各样的模块和组件，这是完全固定的。 好，然后接下来看看 attr 包里面包含了什么，刚才我们引入了 attrs 和 attrib。 首先是 attrs，它主要是用来修饰 class 类的，而 attrib 主要是用来做属性定义的，这个就记住它们两个的用法就好了。 翻了一下源代码，发现其实它还有一些别名：</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">s</span> = attributes = attrs</span><br><span class="line"><span class="attr">ib</span> = attr = attrib</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>也就是说，attrs 可以用 s 或 attributes 来代替，attrib 可以用 attr 或 ib 来代替。 既然是别名，那么上面的类就可以改写成下面的样子：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> s, ib</span><br><span class="line"></span><br><span class="line">@s</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Color</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">r</span> = <span class="symbol">ib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">g</span> = <span class="symbol">ib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">b</span> = <span class="symbol">ib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">color</span> = <span class="symbol">Color</span>(<span class="symbol">255, <span class="symbol">255</span>, <span class="symbol">255</span></span>)</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">color</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>是不是更加简洁了，当然你也可以把 s 改写为 attributes，ib 改写为 attr，随你怎么用啦。 不过我觉得比较舒服的是 attrs 和 attrib 的搭配，感觉可读性更好一些，当然这个看个人喜好。 所以总结一下：</p>
                  <ul>
                    <li>库名：attrs</li>
                    <li>导入包名：attr</li>
                    <li>修饰类：s 或 attributes 或 attrs</li>
                    <li>定义属性：ib 或 attr 或 attrib</li>
                  </ul>
                  <p>OK，理清了这几部分内容，我们继续往下深入了解它的用法吧。</p>
                  <h2 id="声明和比较"><a href="#声明和比较" class="headerlink" title="声明和比较"></a>声明和比较</h2>
                  <p>在这里我们再声明一个简单一点的数据结构，比如叫做 Point，包含 x、y 的坐标，定义如下：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>()</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>()</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>其中 attrib 里面什么参数都没有，如果我们要使用的话，参数可以顺次指定，也可以根据名字指定，如：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">p1 = Point(1, 2)</span><br><span class="line"><span class="builtin-name">print</span>(p1)</span><br><span class="line">p2 = Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=2)</span><br><span class="line"><span class="builtin-name">print</span>(p2)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>其效果都是一样的，打印输出结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=2)</span><br><span class="line">Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=2)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>OK，接下来让我们再验证下类之间的比较方法，由于使用了 attrs，相当于我们定义的类已经有了 <code>__eq__</code>、<code>__ne__</code>、<code>__lt__</code>、<code>__le__</code>、<code>__gt__</code>、<code>__ge__</code> 这几个方法，所以我们可以直接使用比较符来对类和类之间进行比较，下面我们用实例来感受一下：</p>
                  <figure class="highlight lisp">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">print('Equal:', Point(<span class="number">1</span>, <span class="number">2</span>) == Point(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line">print('Not Equal(<span class="name">ne</span>):', Point(<span class="number">1</span>, <span class="number">2</span>) != Point(<span class="number">3</span>, <span class="number">4</span>))</span><br><span class="line">print('Less Than(<span class="name">lt</span>):', Point(<span class="number">1</span>, <span class="number">2</span>) &lt; Point(<span class="number">3</span>, <span class="number">4</span>))</span><br><span class="line">print('Less or Equal(<span class="name">le</span>):', Point(<span class="number">1</span>, <span class="number">2</span>) &lt;= Point(<span class="number">1</span>, <span class="number">4</span>), Point(<span class="number">1</span>, <span class="number">2</span>) &lt;= Point(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line">print('Greater Than(<span class="name">gt</span>):', Point(<span class="number">4</span>, <span class="number">2</span>) &gt; Point(<span class="number">3</span>, <span class="number">2</span>), Point(<span class="number">4</span>, <span class="number">2</span>) &gt; Point(<span class="number">3</span>, <span class="number">1</span>))</span><br><span class="line">print('Greater or Equal(<span class="name">ge</span>):', Point(<span class="number">4</span>, <span class="number">2</span>) &gt;= Point(<span class="number">4</span>, <span class="number">1</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight yaml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">Same:</span> <span class="literal">False</span></span><br><span class="line"><span class="attr">Equal:</span> <span class="literal">True</span></span><br><span class="line"><span class="string">Not</span> <span class="string">Equal(ne):</span> <span class="literal">True</span></span><br><span class="line"><span class="string">Less</span> <span class="string">Than(lt):</span> <span class="literal">True</span></span><br><span class="line"><span class="string">Less</span> <span class="string">or</span> <span class="string">Equal(le):</span> <span class="literal">True</span> <span class="literal">True</span></span><br><span class="line"><span class="string">Greater</span> <span class="string">Than(gt):</span> <span class="literal">True</span> <span class="literal">True</span></span><br><span class="line"><span class="string">Greater</span> <span class="string">or</span> <span class="string">Equal(ge):</span> <span class="literal">True</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可能有的朋友不知道 ne、lt、le 什么的是什么意思，不过看到这里你应该明白啦，ne 就是 Not Equal 的意思，就是不相等，le 就是 Less or Equal 的意思，就是小于或等于。 其内部怎么实现的呢，就是把类的各个属性转成元组来比较了，比如 <code>Point(1, 2) &lt; Point(3, 4)</code> 实际上就是比较了 <code>(1, 2)</code> 和 <code>(3, 4)</code> 两个元组，那么元组之间的比较逻辑又是怎样的呢，这里就不展开了，如果不明白的话可以参考官方文档：<a href="https://docs.python.org/3/library/stdtypes.html#comparisons" target="_blank" rel="noopener">https://docs.python.org/3/library/stdtypes.html#comparisons</a>。</p>
                  <h2 id="属性定义"><a href="#属性定义" class="headerlink" title="属性定义"></a>属性定义</h2>
                  <p>现在看来，对于这个类的定义莫过于每个属性的定义了，也就是 attrib 的定义。对于 attrib 的定义，我们可以传入各种参数，不同的参数对于这个类的定义有非常大的影响。 下面我们就来详细了解一下每个属性的具体参数和用法吧。 首先让我们概览一下总共可能有多少可以控制一个属性的参数，我们用 attrs 里面的 fields 方法可以查看一下：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib, fields</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>()</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>()</span><br><span class="line"></span><br><span class="line"><span class="symbol">print</span>(<span class="symbol">fields</span>(<span class="symbol">Point</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这就可以输出 Point 的所有属性和对应的参数，结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">(Attribute(<span class="attribute">name</span>=<span class="string">'x'</span>, <span class="attribute">default</span>=<span class="literal">NOTHING</span>, <span class="attribute">validator</span>=None, <span class="attribute">repr</span>=<span class="literal">True</span>, <span class="attribute">cmp</span>=<span class="literal">True</span>, <span class="attribute">hash</span>=None, <span class="attribute">init</span>=<span class="literal">True</span>, <span class="attribute">metadata</span>=mappingproxy(&#123;&#125;), <span class="attribute">type</span>=None, <span class="attribute">converter</span>=None, <span class="attribute">kw_only</span>=<span class="literal">False</span>), Attribute(<span class="attribute">name</span>=<span class="string">'y'</span>, <span class="attribute">default</span>=<span class="literal">NOTHING</span>, <span class="attribute">validator</span>=None, <span class="attribute">repr</span>=<span class="literal">True</span>, <span class="attribute">cmp</span>=<span class="literal">True</span>, <span class="attribute">hash</span>=None, <span class="attribute">init</span>=<span class="literal">True</span>, <span class="attribute">metadata</span>=mappingproxy(&#123;&#125;), <span class="attribute">type</span>=None, <span class="attribute">converter</span>=None, <span class="attribute">kw_only</span>=<span class="literal">False</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>输出出来了，可以看到结果是一个元组，元组每一个元素都其实是一个 Attribute 对象，包含了各个参数，下面详细解释下几个参数的含义：</p>
                  <ul>
                    <li>name：属性的名字，是一个字符串类型。</li>
                    <li>default：属性的默认值，如果没有传入初始化数据，那么就会使用默认值。如果没有默认值定义，那么就是 NOTHING，即没有默认值。</li>
                    <li>validator：验证器，检查传入的参数是否合法。</li>
                    <li>init：是否参与初始化，如果为 False，那么这个参数不能当做类的初始化参数，默认是 True。</li>
                    <li>metadata：元数据，只读性的附加数据。</li>
                    <li>type：类型，比如 int、str 等各种类型，默认为 None。</li>
                    <li>converter：转换器，进行一些值的处理和转换器，增加容错性。</li>
                    <li>kw_only：是否为强制关键字参数，默认为 False。</li>
                  </ul>
                  <h3 id="属性名"><a href="#属性名" class="headerlink" title="属性名"></a>属性名</h3>
                  <p>对于属性名，非常清楚了，我们定义什么属性，属性名就是什么，例如上面的例子，定义了：</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">x</span> = attrib()</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>那么其属性名就是 x。</p>
                  <h3 id="默认值"><a href="#默认值" class="headerlink" title="默认值"></a>默认值</h3>
                  <p>对于默认值，如果在初始化的时候没有指定，那么就会默认使用默认值进行初始化，我们看下面的一个实例：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib, fields</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>()</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>(<span class="symbol">default</span>=<span class="symbol">100</span>)</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">Point</span>(<span class="symbol">x</span>=<span class="symbol">1, <span class="symbol">y</span></span>=<span class="symbol">3</span>))</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">Point</span>(<span class="symbol">x</span>=<span class="symbol">1</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>在这里我们将 y 属性的默认值设置为了 100，在初始化的时候，第一次都传入了 x、y 两个参数，第二次只传入了 x 这个参数，看下运行结果：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=3)</span><br><span class="line">Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=100)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到结果，当设置了默认参数的属性没有被传入值时，他就会使用设置的默认值进行初始化。 那假如没有设置默认值但是也没有初始化呢？比如执行下：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="title">Point</span><span class="params">()</span></span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>那么就会报错了，错误如下：</p>
                  <figure class="highlight groovy">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="string">TypeError:</span> __init__() missing <span class="number">1</span> required positional <span class="string">argument:</span> <span class="string">'x'</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>所以说，如果一个属性，我们一旦没有设置默认值同时没有传入的话，就会引起错误。所以，一般来说，为了稳妥起见，设置一个默认值比较好，即使是 None 也可以的。</p>
                  <h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3>
                  <p>如果一个类的某些属性不想参与初始化，比如想直接设置一个初始值，一直固定不变，我们可以将属性的 init 参数设置为 False，看一个实例：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>(<span class="symbol">init</span>=<span class="symbol">False, <span class="symbol">default</span></span>=<span class="symbol">10</span>)</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>()</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">Point</span>(<span class="symbol">3</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>比如 x 我们只想在初始化的时候设置固定值，不想初始化的时候被改变和设定，我们将其设置了 init 参数为 False，同时设置了一个默认值，如果不设置默认值，默认为 NOTHING。然后初始化的时候我们只传入了一个值，其实也就是为 y 这个属性赋值。 这样的话，看下运行结果：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Point(<span class="attribute">x</span>=10, <span class="attribute">y</span>=3)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>没什么问题，y 被赋值为了我们设置的值 3。 那假如我们非要设置 x 呢？会发生什么，比如改写成这样子：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="title">Point</span><span class="params">(<span class="number">1</span>, <span class="number">2</span>)</span></span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>报错了，错误如下：</p>
                  <figure class="highlight yaml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">TypeError:</span> <span class="string">__init__()</span> <span class="string">takes</span> <span class="number">2</span> <span class="string">positional</span> <span class="string">arguments</span> <span class="string">but</span> <span class="number">3</span> <span class="string">were</span> <span class="string">given</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>参数过多，也就是说，已经将 init 设置为 False 的属性就不再被算作可以被初始化的属性了。</p>
                  <h3 id="强制关键字"><a href="#强制关键字" class="headerlink" title="强制关键字"></a>强制关键字</h3>
                  <p>强制关键字是 Python 里面的一个特性，在传入的时候必须使用关键字的名字来传入，如果不太理解可以再了解下 Python 的基础。 设置了强制关键字参数的属性必须要放在后面，其后面不能再有非强制关键字参数的属性，否则会报这样的错误：</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">ValueError: Non keyword-<span class="keyword">only</span> attributes are <span class="keyword">not</span> allowed <span class="keyword">after</span> a keyword-<span class="keyword">only</span> <span class="keyword">attribute</span> (unless they are init=<span class="keyword">False</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>好，我们来看一个例子，我们将最后一个属性设置 kw_only 参数为 True：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib, fields</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>(<span class="symbol">default</span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>(<span class="symbol">kw_only</span>=<span class="symbol">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">Point</span>(<span class="symbol">1, <span class="symbol">y</span></span>=<span class="symbol">3</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果设置了 kw_only 参数为 True，那么在初始化的时候必须传入关键字的名字，这里就必须指定 y 这个名字，运行结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=3)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果没有指定 y 这个名字，像这样调用：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="title">Point</span><span class="params">(<span class="number">1</span>, <span class="number">3</span>)</span></span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>那么就会报错：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">TypeError: __init__() takes <span class="keyword">from</span> <span class="number">1</span> to <span class="number">2</span> positional arguments but <span class="number">3</span> were given</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>所以，这个参数就是设置初始化传参必须要用名字来传，否则会出现错误。 注意，如果我们将一个属性设置了 init 为 False，那么 kw_only 这个参数会被忽略。</p>
                  <h3 id="验证器"><a href="#验证器" class="headerlink" title="验证器"></a>验证器</h3>
                  <p>有时候在设置一个属性的时候必须要满足某个条件，比如性别必须要是男或者女，否则就不合法。对于这种情况，我们就需要有条件来控制某些属性不能为非法值。 下面我们看一个实例：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"></span><br><span class="line">def is_valid_gender(instance, attribute, value):</span><br><span class="line">    <span class="keyword">if</span> value <span class="keyword">not</span> <span class="keyword">in</span> [<span class="string">'male'</span>, <span class="string">'female'</span>]:</span><br><span class="line">        raise ValueError(f<span class="string">'gender &#123;value&#125; is not valid'</span>)</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Person</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">name</span> = <span class="symbol">attrib</span>()</span><br><span class="line">    <span class="symbol">gender</span> = <span class="symbol">attrib</span>(<span class="symbol">validator</span>=<span class="symbol">is_valid_gender</span>)</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">Person</span>(<span class="symbol">name</span>='<span class="symbol">Mike</span>', <span class="symbol">gender</span>='<span class="symbol">male</span>'))</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">Person</span>(<span class="symbol">name</span>='<span class="symbol">Mike</span>', <span class="symbol">gender</span>='<span class="symbol">mlae</span>'))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>在这里我们定义了一个验证器 Validator 方法，叫做 is_valid_gender。然后定义了一个类 Person 还有它的两个属性 name 和 gender，其中 gender 定义的时候传入了一个参数 validator，其值就是我们定义的 Validator 方法。 这个 Validator 定义的时候有几个固定的参数：</p>
                  <ul>
                    <li>instance：类对象</li>
                    <li>attribute：属性名</li>
                    <li>value：属性值</li>
                  </ul>
                  <p>这是三个参数是固定的，在类初始化的时候，其内部会将这三个参数传递给这个 Validator，因此 Validator 里面就可以接受到这三个值，然后进行判断即可。在 Validator 里面，我们判断如果不是男性或女性，那么就直接抛出错误。 下面做了两个实验，一个就是正常传入 male，另一个写错了，写的是 mlae，观察下运行结果：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Person(<span class="attribute">name</span>=<span class="string">'Mike'</span>, <span class="attribute">gender</span>=<span class="string">'male'</span>)</span><br><span class="line">TypeError: __init__() missing 1 required positional argument: <span class="string">'gender'</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>OK，结果显而易见了，第二个报错了，因为其值不是正常的性别，所以程序直接报错终止。 注意在 Validator 里面返回 True 或 False 是没用的，错误的值还会被照常复制。所以，一定要在 Validator 里面 raise 某个错误。 另外 attrs 库里面还给我们内置了好多 Validator，比如判断类型，这里我们再增加一个属性 age，必须为 int 类型：</p>
                  <figure class="highlight lisp">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">age = attrib(<span class="name">validator=validators</span>.instance_of(<span class="name">int</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这时候初始化的时候就必须传入 int 类型，如果为其他类型，则直接抛错：</p>
                  <figure class="highlight less">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attribute">TypeError</span>: ("<span class="string">'age'</span> must be &lt;class <span class="string">'int'</span>&gt; (got <span class="string">'x'</span> that is a &lt;class <span class="string">'str'</span>&gt;).</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>另外还有其他的一些 Validator，比如与或运算、可执行判断、可迭代判断等等，可以参考官方文档：<a href="https://www.attrs.org/en/stable/api.html#validators" target="_blank" rel="noopener">https://www.attrs.org/en/stable/api.html#validators</a>。 另外 validator 参数还支持多个 Validator，比如我们要设置既要是数字，又要小于 100，那么可以把几个 Validator 放到一个列表里面并传入：</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from attr import attrs, attrib, validators</span><br><span class="line"></span><br><span class="line">def is<span class="constructor">_less_than_100(<span class="params">instance</span>, <span class="params">attribute</span>, <span class="params">value</span>)</span>:</span><br><span class="line">    <span class="keyword">if</span> value &gt; <span class="number">100</span>:</span><br><span class="line">        raise <span class="constructor">ValueError(<span class="params">f</span>'<span class="params">age</span> &#123;<span class="params">value</span>&#125; <span class="params">must</span> <span class="params">less</span> <span class="params">than</span> 100')</span></span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="constructor">Person(<span class="params">object</span>)</span>:</span><br><span class="line">    name = attrib<span class="literal">()</span></span><br><span class="line">    gender = attrib(validator=is_valid_gender)</span><br><span class="line">    age = attrib(validator=<span class="literal">[<span class="identifier">validators</span>.<span class="identifier">instance_of</span>(<span class="identifier">int</span>), <span class="identifier">is_less_than_100</span>]</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__<span class="operator"> == </span>'__main__':</span><br><span class="line">    print(<span class="constructor">Person(<span class="params">name</span>='Mike', <span class="params">gender</span>='<span class="params">male</span>', <span class="params">age</span>=500)</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样就会将所有的 Validator 都执行一遍，必须每个 Validator 都满足才可以。这里 age 传入了 500，那么不符合第二个 Validator，直接抛错：</p>
                  <figure class="highlight yaml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">ValueError:</span> <span class="string">age</span> <span class="number">500</span> <span class="string">must</span> <span class="string">less</span> <span class="string">than</span> <span class="number">100</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="转换器"><a href="#转换器" class="headerlink" title="转换器"></a>转换器</h3>
                  <p>其实很多时候我们会不小心传入一些形式不太标准的结果，比如本来是 int 类型的 100，我们传入了字符串类型的 100，那这时候直接抛错应该不好吧，所以我们可以设置一些转换器来增强容错机制，比如将字符串自动转为数字等等，看一个实例：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"></span><br><span class="line">def to_int(value):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">int</span>(value)</span><br><span class="line">    except:</span><br><span class="line">        <span class="keyword">return</span> None</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>(<span class="symbol">converter</span>=<span class="symbol">to_int</span>)</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>()</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">Point</span>('<span class="symbol">100</span>', <span class="symbol">3</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>看这里，我们定义了一个方法，可以将值转化为数字类型，如果不能转，那么就返回 None，这样保证了任何可以被转数字的值都被转为数字，否则就留空，容错性非常高。 运行结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Point(<span class="attribute">x</span>=100, <span class="attribute">y</span>=3)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="类型"><a href="#类型" class="headerlink" title="类型"></a>类型</h3>
                  <p>为什么把这个放到最后来讲呢，因为 Python 中的类型是非常复杂的，有原生类型，有 typing 类型，有自定义类的类型。 首先我们来看看原生类型是怎样的，这个很容易理解了，就是普通的 int、float、str 等类型，其定义如下：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int</span>)</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>()</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">Point</span>(<span class="symbol">100, <span class="symbol">3</span></span>))</span><br><span class="line">    <span class="symbol">print</span>(<span class="symbol">Point</span>('<span class="symbol">100</span>', <span class="symbol">3</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们将 x 属性定义为 int 类型了，初始化的时候传入了数值型 100 和字符串型 100，结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Point(<span class="attribute">x</span>=100, <span class="attribute">y</span>=3)</span><br><span class="line">Point(<span class="attribute">x</span>=<span class="string">'100'</span>, <span class="attribute">y</span>=3)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>但我们发现，虽然定义了，但是不会被自动转类型的。 另外我们还可以自定义 typing 里面的类型，比如 List，另外 attrs 里面也提供了类型的定义：</p>
                  <figure class="highlight elm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="title">from</span> attr <span class="keyword">import</span> attrs, attrib, Factory</span><br><span class="line"><span class="keyword">import</span> typing</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="title">class</span> <span class="type">Point</span>(object):</span><br><span class="line">    x = attrib(<span class="keyword">type</span>=int)</span><br><span class="line">    y = attrib(<span class="keyword">type</span>=typing.<span class="type">List</span>[int])</span><br><span class="line">    z = attrib(<span class="keyword">type</span>=<span class="type">Factory</span>(list))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们引入了 typing 这个包，定义了 y 为 int 数字组成的列表，z 使用了 attrs 里面定义的 Factory 定义了同样为列表类型。 另外我们也可以进行类型的嵌套，比如像这样子：</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib, Factory</span><br><span class="line"><span class="keyword">import</span> typing</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="type">Point</span>(<span class="keyword">object</span>):</span><br><span class="line">    x = attrib(<span class="keyword">type</span>=<span class="type">int</span>, default=<span class="number">0</span>)</span><br><span class="line">    y = attrib(<span class="keyword">type</span>=<span class="type">int</span>, default=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> Line(<span class="keyword">object</span>):</span><br><span class="line">    <span class="type">name</span> = attrib()</span><br><span class="line">    points = attrib(<span class="keyword">type</span>=typing.List[<span class="type">Point</span>])</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    points = [<span class="type">Point</span>(i, i) <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">5</span>)]</span><br><span class="line">    print(points)</span><br><span class="line">    <span class="type">line</span> = Line(<span class="type">name</span>=<span class="string">'line1'</span>, points=points)</span><br><span class="line">    print(<span class="type">line</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>在这里我们定义了 Point 类代表离散点，随后定义了线，其拥有 points 属性是 Point 组成的列表。在初始化的时候我们声明了五个点，然后用这五个点组成的列表声明了一条线，逻辑没什么问题。 运行结果：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[Point(<span class="attribute">x</span>=0, <span class="attribute">y</span>=0), Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=1), Point(<span class="attribute">x</span>=2, <span class="attribute">y</span>=2), Point(<span class="attribute">x</span>=3, <span class="attribute">y</span>=3), Point(<span class="attribute">x</span>=4, <span class="attribute">y</span>=4)]</span><br><span class="line">Line(<span class="attribute">name</span>=<span class="string">'line1'</span>, points=[Point(<span class="attribute">x</span>=0, <span class="attribute">y</span>=0), Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=1), Point(<span class="attribute">x</span>=2, <span class="attribute">y</span>=2), Point(<span class="attribute">x</span>=3, <span class="attribute">y</span>=3), Point(<span class="attribute">x</span>=4, <span class="attribute">y</span>=4)])</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到这里我们得到了一个嵌套类型的 Line 对象，其值是 Point 类型组成的列表。 以上便是一些属性的定义，把握好这些属性的定义，我们就可以非常方便地定义一个类了。</p>
                  <h2 id="序列转换"><a href="#序列转换" class="headerlink" title="序列转换"></a>序列转换</h2>
                  <p>在很多情况下，我们经常会遇到 JSON 等字符串序列和对象互相转换的需求，尤其是在写 REST API、数据库交互的时候。 attrs 库的存在让我们可以非常方便地定义 Python 类，但是它对于序列字符串的转换功能还是比较薄弱的，cattrs 这个库就是用来弥补这个缺陷的，下面我们再来看看 cattrs 这个库。 cattrs 导入的时候名字也不太一样，叫做 cattr，它里面提供了两个主要的方法，叫做 structure 和 unstructure，两个方法是相反的，对于类的序列化和反序列化支持非常好。</p>
                  <h3 id="基本转换"><a href="#基本转换" class="headerlink" title="基本转换"></a>基本转换</h3>
                  <p>首先我们来看看基本的转换方法的用法，看一个基本的转换实例：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"><span class="keyword">from</span> cattr <span class="keyword">import</span> unstructure, structure</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">point</span> = <span class="symbol">Point</span>(<span class="symbol">x</span>=<span class="symbol">1, <span class="symbol">y</span></span>=<span class="symbol">2</span>)</span><br><span class="line">    <span class="symbol">json</span> = <span class="symbol">unstructure</span>(<span class="symbol">point</span>)</span><br><span class="line">    <span class="symbol">print</span>('<span class="symbol">json:</span>', <span class="symbol">json</span>)</span><br><span class="line">    <span class="symbol">obj</span> = <span class="symbol">structure</span>(<span class="symbol">json, <span class="symbol">Point</span></span>)</span><br><span class="line">    <span class="symbol">print</span>('<span class="symbol">obj:</span>', <span class="symbol">obj</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>在这里我们定义了一个 Point 对象，然后调用 unstructure 方法即可直接转换为 JSON 字符串。如果我们再想把它转回来，那就需要调用 structure 方法，这样就成功转回了一个 Point 对象。 看下运行结果：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">json: &#123;<span class="string">'x'</span>: 1, <span class="string">'y'</span>: 2&#125;</span><br><span class="line">obj: Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=2)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>当然这种基本的来回转用的多了就轻车熟路了。</p>
                  <h3 id="多类型转换"><a href="#多类型转换" class="headerlink" title="多类型转换"></a>多类型转换</h3>
                  <p>另外 structure 也支持一些其他的类型转换，看下实例：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">\&gt;&gt;&gt; cattr.structure(<span class="number">1</span>, str)</span><br><span class="line"><span class="string">'1'</span></span><br><span class="line">&gt;&gt;&gt; cattr.structure(<span class="string">"1"</span>, <span class="built_in">float</span>)</span><br><span class="line"><span class="number">1.0</span></span><br><span class="line">&gt;&gt;&gt; cattr.structure([<span class="number">1.0</span>, <span class="number">2</span>, <span class="string">"3"</span>], Tuple[<span class="built_in">int</span>, <span class="built_in">int</span>, <span class="built_in">int</span>])</span><br><span class="line">(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line">&gt;&gt;&gt; cattr.structure((<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>), MutableSequence[<span class="built_in">int</span>])</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">&gt;&gt;&gt; cattr.structure((<span class="number">1</span>, None, <span class="number">3</span>), List[Optional[str]])</span><br><span class="line">[<span class="string">'1'</span>, None, <span class="string">'3'</span>]</span><br><span class="line">&gt;&gt;&gt; cattr.structure([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>], Set)</span><br><span class="line">&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;</span><br><span class="line">&gt;&gt;&gt; cattr.structure([[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>]], Set[FrozenSet[str]])</span><br><span class="line">&#123;frozenset(&#123;<span class="string">'4'</span>, <span class="string">'3'</span>&#125;), frozenset(&#123;<span class="string">'1'</span>, <span class="string">'2'</span>&#125;)&#125;</span><br><span class="line">&gt;&gt;&gt; cattr.structure(OrderedDict([(<span class="number">1</span>, <span class="number">2</span>), (<span class="number">3</span>, <span class="number">4</span>)]), Dict)</span><br><span class="line">&#123;<span class="number">1</span>: <span class="number">2</span>, <span class="number">3</span>: <span class="number">4</span>&#125;</span><br><span class="line">&gt;&gt;&gt; cattr.structure([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], Tuple[<span class="built_in">int</span>, str, <span class="built_in">float</span>])</span><br><span class="line">(<span class="number">1</span>, <span class="string">'2'</span>, <span class="number">3.0</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里面用到了 Tuple、MutableSequence、Optional、Set 等类，都属于 typing 这个模块，后面我会写内容详细介绍这个库的用法。 不过总的来说，大部分情况下，JSON 和对象的互转是用的最多的。</p>
                  <h3 id="属性处理"><a href="#属性处理" class="headerlink" title="属性处理"></a>属性处理</h3>
                  <p>上面的例子都是理想情况下使用的，但在实际情况下，很容易遇到 JSON 和对象不对应的情况，比如 JSON 多个字段，或者对象多个字段。 我们先看看下面的例子：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"><span class="keyword">from</span> cattr <span class="keyword">import</span> structure</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="symbol">json</span> = &#123;<span class="string">'x'</span>: <span class="number">1</span>, <span class="string">'y'</span>: <span class="number">2</span>, <span class="string">'z'</span>: <span class="number">3</span>&#125;</span><br><span class="line">print(structure(json, Point))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>在这里，JSON 多了一个字段 z，而 Point 类只有 x、y 两个字段，那么直接执行 structure 会出现什么情况呢？</p>
                  <figure class="highlight avrasm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="symbol">TypeError:</span> __init__() got an unexpected keyword argument <span class="string">'z'</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>不出所料，报错了。意思是多了一个参数，这个参数并没有被定义。 这时候一般的解决方法的直接忽略这个参数，可以重写一下 structure 方法，定义如下：</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">def drop<span class="constructor">_nonattrs(<span class="params">d</span>, <span class="params">type</span>)</span>:</span><br><span class="line">    <span class="keyword">if</span> not isinstance(d, dict): return d</span><br><span class="line">    attrs_attrs = getattr(<span class="keyword">type</span>, '__attrs_attrs__', None)</span><br><span class="line">    <span class="keyword">if</span> attrs_attrs is None:</span><br><span class="line">        raise <span class="constructor">ValueError(<span class="params">f</span>'<span class="params">type</span> &#123;<span class="params">type</span>&#125; <span class="params">is</span> <span class="params">not</span> <span class="params">an</span> <span class="params">attrs</span> <span class="params">class</span>')</span></span><br><span class="line">    attrs: Set<span class="literal">[<span class="identifier">str</span>]</span> = &#123;attr.name for attr <span class="keyword">in</span> attrs_attrs&#125;</span><br><span class="line">    return &#123;key: <span class="keyword">val</span> for key, <span class="keyword">val</span> <span class="keyword">in</span> d.items<span class="literal">()</span> <span class="keyword">if</span> key <span class="keyword">in</span> attrs&#125;</span><br><span class="line"></span><br><span class="line">def structure(d, <span class="keyword">type</span>):</span><br><span class="line">    return cattr.structure(drop<span class="constructor">_nonattrs(<span class="params">d</span>, <span class="params">type</span>)</span>, <span class="keyword">type</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里定义了一个 drop_nonattrs 方法，用于从 JSON 里面删除对象里面不存在的属性，然后调用新的 structure 方法即可，写法如下：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Set</span><br><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"><span class="keyword">import</span> cattr</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="symbol">def</span> <span class="symbol">drop_nonattrs</span>(<span class="symbol">d, <span class="symbol">type</span></span>):</span><br><span class="line">    <span class="symbol">if</span> <span class="symbol">not</span> <span class="symbol">isinstance</span>(<span class="symbol">d, <span class="symbol">dict</span></span>): <span class="symbol">return</span> <span class="symbol">d</span></span><br><span class="line">    <span class="symbol">attrs_attrs</span> = <span class="symbol">getattr</span>(<span class="symbol">type, </span>'<span class="symbol">__attrs_attrs__</span>', <span class="symbol">None</span>)</span><br><span class="line">    <span class="symbol">if</span> <span class="symbol">attrs_attrs</span> <span class="symbol">is</span> <span class="symbol">None:</span></span><br><span class="line">        <span class="symbol">raise</span> <span class="symbol">ValueError</span>(<span class="symbol">f</span>'<span class="symbol">type</span> &#123;type&#125; <span class="keyword">is</span> <span class="keyword">not</span> an attrs <span class="keyword">class</span>')</span><br><span class="line">    <span class="symbol">attrs: <span class="symbol">Set</span></span>[<span class="symbol">str</span>] = &#123;attr.name <span class="keyword">for</span> attr <span class="keyword">in</span> attrs_attrs&#125;</span><br><span class="line">    <span class="keyword">return</span> &#123;key: val <span class="keyword">for</span> key, val <span class="keyword">in</span> d.items() <span class="keyword">if</span> key <span class="keyword">in</span> attrs&#125;</span><br><span class="line"></span><br><span class="line">def structure(d, type):</span><br><span class="line">    <span class="keyword">return</span> cattr.structure(drop_nonattrs(d, type), type)</span><br><span class="line"></span><br><span class="line">json = &#123;<span class="string">'x'</span>: <span class="number">1</span>, <span class="string">'y'</span>: <span class="number">2</span>, <span class="string">'z'</span>: <span class="number">3</span>&#125;</span><br><span class="line">print(structure(json, Point))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样我们就可以避免 JSON 字段冗余导致的转换问题了。 另外还有一个常见的问题，那就是数据对象转换，比如对于时间来说，在对象里面声明我们一般会声明为 datetime 类型，但在序列化的时候却需要序列化为字符串。 所以，对于一些特殊类型的属性，我们往往需要进行特殊处理，这时候就需要我们针对某种特定的类型定义特定的 hook 处理方法，这里就需要用到 register_unstructure_hook 和 register_structure_hook 方法了。 下面这个例子是时间 datetime 转换的时候进行的处理：</p>
                  <figure class="highlight haskell">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> datetime</span><br><span class="line"><span class="title">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"><span class="keyword">import</span> cattr</span><br><span class="line"></span><br><span class="line"><span class="type">TIME_FORMAT</span> = '%<span class="type">Y</span>-%m-%dT%<span class="type">H</span>:%<span class="type">M</span>:%<span class="type">S</span>.%fZ'</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">Event</span>(<span class="title">object</span>):</span></span><br><span class="line"><span class="class">    happened_at = attrib(<span class="title">type</span>=<span class="title">datetime</span>.<span class="title">datetime</span>)</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class">cattr.register_unstructure_hook(<span class="title">datetime</span>.<span class="title">datetime</span>, <span class="title">lambda</span> <span class="title">dt</span>: <span class="title">dt</span>.<span class="title">strftime</span>(<span class="type">TIME_FORMAT</span>))</span></span><br><span class="line"><span class="class">cattr.register_structure_hook(<span class="title">datetime</span>.<span class="title">datetime</span>,</span></span><br><span class="line"><span class="class">                              <span class="title">lambda</span> <span class="title">string</span>, <span class="title">_</span>: <span class="title">datetime</span>.<span class="title">datetime</span>.<span class="title">strptime</span>(<span class="title">string</span>, <span class="type">TIME_FORMAT</span>))</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class">event = <span class="type">Event</span>(<span class="title">happened_at</span>=<span class="title">datetime</span>.<span class="title">datetime</span>(2019, 6, 1))</span></span><br><span class="line"><span class="class">print('<span class="title">event</span>:', <span class="title">event</span>)</span></span><br><span class="line"><span class="class">json = cattr.unstructure(<span class="title">event</span>)</span></span><br><span class="line"><span class="class">print('<span class="title">json</span>:', <span class="title">json</span>)</span></span><br><span class="line"><span class="class">event = cattr.structure(<span class="title">json</span>, <span class="type">Event</span>)</span></span><br><span class="line"><span class="class">print('<span class="type">Event</span>:', <span class="title">event</span>)</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>在这里我们对 datetime 这个类型注册了两个 hook，当序列化的时候，就调用 strftime 方法转回字符串，当反序列化的时候，就调用 strptime 将其转回 datetime 类型。 看下运行结果：</p>
                  <figure class="highlight yaml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">event:</span> <span class="string">Event(happened_at=datetime.datetime(2019,</span> <span class="number">6</span><span class="string">,</span> <span class="number">1</span><span class="string">,</span> <span class="number">0</span><span class="string">,</span> <span class="number">0</span><span class="string">))</span></span><br><span class="line"><span class="attr">json:</span> <span class="string">&#123;'happened_at':</span> <span class="string">'2019-06-01T00:00:00.000000Z'</span><span class="string">&#125;</span></span><br><span class="line"><span class="attr">Event:</span> <span class="string">Event(happened_at=datetime.datetime(2019,</span> <span class="number">6</span><span class="string">,</span> <span class="number">1</span><span class="string">,</span> <span class="number">0</span><span class="string">,</span> <span class="number">0</span><span class="string">))</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样对于一些特殊类型的属性处理也得心应手了。</p>
                  <h3 id="嵌套处理"><a href="#嵌套处理" class="headerlink" title="嵌套处理"></a>嵌套处理</h3>
                  <p>最后我们再来看看嵌套类型的处理，比如类里面有个属性是另一个类的类型，如果遇到这种嵌套类的话，怎样类转转换呢？我们用一个实例感受下：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> attr <span class="keyword">import</span> attrs, attrib</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> List</span><br><span class="line"><span class="keyword">from</span> cattr <span class="keyword">import</span> structure, unstructure</span><br><span class="line"></span><br><span class="line">@attrs</span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Point</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">x</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">y</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">int, <span class="symbol">default</span></span>=<span class="symbol">0</span>)</span><br><span class="line"></span><br><span class="line">@<span class="symbol">attrs</span></span><br><span class="line"><span class="symbol">class</span> <span class="symbol">Color</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">r</span> = <span class="symbol">attrib</span>(<span class="symbol">default</span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">g</span> = <span class="symbol">attrib</span>(<span class="symbol">default</span>=<span class="symbol">0</span>)</span><br><span class="line">    <span class="symbol">b</span> = <span class="symbol">attrib</span>(<span class="symbol">default</span>=<span class="symbol">0</span>)</span><br><span class="line"></span><br><span class="line">@<span class="symbol">attrs</span></span><br><span class="line"><span class="symbol">class</span> <span class="symbol">Line</span>(<span class="symbol">object</span>):</span><br><span class="line">    <span class="symbol">color</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">Color</span>)</span><br><span class="line">    <span class="symbol">points</span> = <span class="symbol">attrib</span>(<span class="symbol">type</span>=<span class="symbol">List</span>[<span class="symbol">Point</span>])</span><br><span class="line"></span><br><span class="line"><span class="symbol">if</span> <span class="symbol">__name__</span> == '<span class="symbol">__main__</span>':</span><br><span class="line">    <span class="symbol">line</span> = <span class="symbol">Line</span>(<span class="symbol">color</span>=<span class="symbol">Color</span>(), <span class="symbol">points</span>=[<span class="symbol">Point</span>(<span class="symbol">i, <span class="symbol">i</span></span>) <span class="symbol">for</span> <span class="symbol">i</span> <span class="symbol">in</span> <span class="symbol">range</span>(<span class="symbol">5</span>)])</span><br><span class="line">    <span class="symbol">print</span>('<span class="symbol">Object:</span>', <span class="symbol">line</span>)</span><br><span class="line">    <span class="symbol">json</span> = <span class="symbol">unstructure</span>(<span class="symbol">line</span>)</span><br><span class="line">    <span class="symbol">print</span>('<span class="symbol">JSON:</span>', <span class="symbol">json</span>)</span><br><span class="line">    <span class="symbol">line</span> = <span class="symbol">structure</span>(<span class="symbol">json, <span class="symbol">Line</span></span>)</span><br><span class="line">    <span class="symbol">print</span>('<span class="symbol">Object:</span>', <span class="symbol">line</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们定义了两个 Class，一个是 Point，一个是 Color，然后定义了 Line 对象，其属性类型一个是 Color 类型，一个是 Point 类型组成的列表，下面我们进行序列化和反序列化操作，转成 JSON 然后再由 JSON 转回来，运行结果如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Object: Line(<span class="attribute">color</span>=Color(r=0, <span class="attribute">g</span>=0, <span class="attribute">b</span>=0), points=[Point(<span class="attribute">x</span>=0, <span class="attribute">y</span>=0), Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=1), Point(<span class="attribute">x</span>=2, <span class="attribute">y</span>=2), Point(<span class="attribute">x</span>=3, <span class="attribute">y</span>=3), Point(<span class="attribute">x</span>=4, <span class="attribute">y</span>=4)])</span><br><span class="line">JSON: &#123;<span class="string">'color'</span>: &#123;<span class="string">'r'</span>: 0, <span class="string">'g'</span>: 0, <span class="string">'b'</span>: 0&#125;, <span class="string">'points'</span>: [&#123;<span class="string">'x'</span>: 0, <span class="string">'y'</span>: 0&#125;, &#123;<span class="string">'x'</span>: 1, <span class="string">'y'</span>: 1&#125;, &#123;<span class="string">'x'</span>: 2, <span class="string">'y'</span>: 2&#125;, &#123;<span class="string">'x'</span>: 3, <span class="string">'y'</span>: 3&#125;, &#123;<span class="string">'x'</span>: 4, <span class="string">'y'</span>: 4&#125;]&#125;</span><br><span class="line">Object: Line(<span class="attribute">color</span>=Color(r=0, <span class="attribute">g</span>=0, <span class="attribute">b</span>=0), points=[Point(<span class="attribute">x</span>=0, <span class="attribute">y</span>=0), Point(<span class="attribute">x</span>=1, <span class="attribute">y</span>=1), Point(<span class="attribute">x</span>=2, <span class="attribute">y</span>=2), Point(<span class="attribute">x</span>=3, <span class="attribute">y</span>=3), Point(<span class="attribute">x</span>=4, <span class="attribute">y</span>=4)])</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到，我们非常方便地将对象转化为了 JSON 对象，然后也非常方便地转回了对象。 这样我们就成功实现了嵌套对象的序列化和反序列化，所有问题成功解决！</p>
                  <h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2>
                  <p>本节介绍了利用 attrs 和 cattrs 两个库实现 Python 面向对象编程的实践，有了它们两个的加持，Python 面向对象编程不再是难事。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-06-18 17:21:02" itemprop="dateCreated datePublished" datetime="2019-06-18T17:21:02+08:00">2019-06-18</time>
                </span>
                <span id="/6552.html" class="post-meta-item leancloud_visitors" data-flag-title="Python 使用 attrs 和 cattrs 实现面向对象编程" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>18k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>16 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6607.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6607.html" class="post-title-link" itemprop="url">代码整洁之道-编写 Pythonic 代码</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>很多新手在开始学一门新的语言的时候，往往会忽视一些不应该忽视的细节，比如变量命名和函数命名以及注释等一些内容的规范性，久而久之养成了一种习惯。对此呢，我特意收集了一些适合所有学习 Python 的人，代码整洁之道。</p>
                  <h3 id="写出-Pythonic-代码"><a href="#写出-Pythonic-代码" class="headerlink" title="写出 Pythonic 代码"></a><strong>写出 Pythonic 代码</strong></h3>
                  <p>谈到规范首先想到就是 Python 有名的 PEP8 代码规范文档，它定义了编写 Pythonic 代码的最佳实践。可以在 <a href="https://link.zhihu.com/?target=https%3A//www.python.org/dev/peps/pep-0008/">https://www.python.org/dev/peps/pep-0008/</a> 上查看。但是真正去仔细研究学习这些规范的朋友并不是很多，对此呢这篇文章摘选一些比较常用的代码整洁和规范的技巧和方法，下面让我们一起来学习吧！</p>
                  <h3 id="命名"><a href="#命名" class="headerlink" title="命名"></a><strong>命名</strong></h3>
                  <p>所有的编程语言都有变量、函数、类等的命名约定，以美之称的 Python 当然更建议使用命名约定。 接下来就针对类、函数、方法等等内容进行学习。</p>
                  <h3 id="变量和函数"><a href="#变量和函数" class="headerlink" title="变量和函数"></a><strong>变量和函数</strong></h3>
                  <p>使用小写字母命名函数和变量，并用下划线分隔单词，提高代码可读性。</p>
                  <h3 id="变量的声明"><a href="#变量的声明" class="headerlink" title="变量的声明"></a><strong>变量的声明</strong></h3>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">names</span> = <span class="string">"Python"</span> <span class="comment">#变量名 </span></span><br><span class="line"><span class="attr">namejob_title</span> = <span class="string">"Software Engineer"</span> <span class="comment">#带有下划线的变量名</span></span><br><span class="line"><span class="attr">populated_countries_list</span> = []  <span class="comment">#带有下划线的变量名</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>还应该考虑在代码中使用非 Python 内置方法名，如果使用 Python 中内置方法名请使用一个或两个下划线()。</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">_books</span> = &#123;&#125;<span class="comment"># 变量名私有化</span></span><br><span class="line"><span class="attr">__dict</span> = []<span class="comment"># 防止python内置库中的名称混淆</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>那如何选择是用<em>还是__呢？ 如果不希望外部类访问该变量，应该使用一个下划线（</em>）作为类的内部变量的前缀。如果要定义的私有变量名称是 Python 中的关键字如 dict 就要使用(__)。</p>
                  <h3 id="函数的声明"><a href="#函数的声明" class="headerlink" title="函数的声明"></a><strong>函数的声明</strong></h3>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_data</span><span class="params">()</span>:</span> </span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">calculate_tax_data</span><span class="params">()</span>:</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>函数的声明和变量一样也是通过小写字母和单下划线进行连接。 当然对于函数私有化也是和声明变量类似。</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_get_data</span><span class="params">()</span>:</span></span><br><span class="line">   <span class="keyword">pass</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>函数的开头使用单下划线，将其进行私有化。对于使用 Pyton 中的关键字来进行命名的函数 要使用双下划线。</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">__path</span><span class="params">()</span>:</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>除了遵循这些命名规则之外，使用清晰易懂的变量名和很重要。</p>
                  <h3 id="函数名规范"><a href="#函数名规范" class="headerlink" title="函数名规范"></a><strong>函数名规范</strong></h3>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="comment"># Wrong Way</span></span><br><span class="line">def get_user_info(id):</span><br><span class="line">   db = get_db_connection()</span><br><span class="line">  <span class="built_in"> user </span>= execute_query_for_user(id)</span><br><span class="line">   return user</span><br><span class="line"></span><br><span class="line"><span class="comment"># Right way</span></span><br><span class="line">def get_user_by(user_id):</span><br><span class="line">    db = get_db_connection()</span><br><span class="line">   <span class="built_in"> user </span>= execute_user_query(user_id) </span><br><span class="line">    return user</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里，第二个函数 get_user_by 确保使用相同的参数来传递变量，从而为函数提供正确的上下文。 第一个函数 get_user_info 就不怎么不明确了，因为参数 id 意味着什么这里我们不能确定，它是用户 ID，还是用户付款ID或任何其他 ID？ 这种代码可能会对使用你的API的其他开发人员造成混淆。为了解决这个问题，我在第二个函数中更改了两个东西; 我更改了函数名称以及传递的参数名称，这使代码可读性更高。 作为开发人员，你有责任在命名变量和函数时仔细考虑，要写让人能够清晰易懂的代码。 当然也方便自己以后去维护。</p>
                  <h3 id="类的命名规范"><a href="#类的命名规范" class="headerlink" title="类的命名规范"></a><strong>类的命名规范</strong></h3>
                  <p>类的名称应该像大多数其他语言一样使用驼峰大小写。</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">class</span> <span class="symbol">UserInformation:</span></span><br><span class="line">     <span class="symbol">def</span> <span class="symbol">get_user</span>(<span class="symbol">id</span>):</span><br><span class="line">          <span class="symbol">db</span> = <span class="symbol">get_db_connection</span>()</span><br><span class="line">          <span class="symbol">user</span> = <span class="symbol">execute_query_for_user</span>(<span class="symbol">id</span>)</span><br><span class="line">          <span class="symbol">return</span> <span class="symbol">user</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="常量的命名规范"><a href="#常量的命名规范" class="headerlink" title="常量的命名规范"></a><strong>常量的命名规范</strong></h3>
                  <p>通常应该用大写字母定义常量名称。</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">TOTAL</span> = <span class="number">56</span></span><br><span class="line"><span class="attr">TIMOUT</span> = <span class="number">6</span></span><br><span class="line"><span class="attr">MAX_OVERFLOW</span> = <span class="number">7</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="函数和方法的参数"><a href="#函数和方法的参数" class="headerlink" title="函数和方法的参数"></a><strong>函数和方法的参数</strong></h3>
                  <p>函数和方法的参数命名应遵循与变量和方法名称相同的规则。因为类方法将self作为第一个关键字参数。所以在函数中就不要使用 self 作为关键字作为参数，以免造成混淆 .</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">def calculate_tax(amount, yearly_tax):</span><br><span class="line">    passs</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="symbol">Player:</span></span><br><span class="line">    <span class="symbol">def</span> <span class="symbol">get_total_score</span>(<span class="symbol">self, <span class="symbol">player_name</span></span>):</span><br><span class="line">         <span class="symbol">pass</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>关于命名大概就强调这些，下面让我们看看表达式和语句中需要的问题。</p>
                  <h3 id="代码中的表达式和语句"><a href="#代码中的表达式和语句" class="headerlink" title="代码中的表达式和语句"></a><strong>代码中的表达式和语句</strong></h3>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">users = [</span><br><span class="line">    &#123;<span class="string">"first_name"</span>:<span class="string">"Helen"</span>, <span class="string">"age"</span>:39&#125;,</span><br><span class="line">    &#123;<span class="string">"first_name"</span>:<span class="string">"Buck"</span>, <span class="string">"age"</span>:10&#125;,</span><br><span class="line">    &#123;<span class="string">"first_name"</span>:<span class="string">"anni"</span>, <span class="string">"age"</span>:9&#125;</span><br><span class="line">]</span><br><span class="line">users = sorted(users, <span class="attribute">key</span>=lambda user: user[<span class="string">"first_name"</span>].lower())</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这段代码有什么问题? 乍一看并不容易理解这段代码，尤其是对于新开发人员来说，因为 lambdas 的语法很古怪，所以不容易理解。虽然这里使用 lambda 可以节省行，然而，这并不能保证代码的正确性和可读性。同时这段代码无法解决字典缺少键出现异常的问题。 让我们使用函数重写此代码，使代码更具可读性和正确性; 该函数将判断异常情况，编写起来要简单得多。</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">users = [</span><br><span class="line">    &#123;<span class="string">"first_name"</span>:<span class="string">"Helen"</span>, <span class="string">"age"</span>:<span class="number">39</span>&#125;,</span><br><span class="line">    &#123;<span class="string">"first_name"</span>:<span class="string">"Buck"</span>, <span class="string">"age"</span>:<span class="number">10</span>&#125;,</span><br><span class="line">    &#123;<span class="string">"name"</span>:<span class="string">"anni"</span>, <span class="string">"age"</span>:<span class="number">9</span>&#125;</span><br><span class="line">]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_user_name</span><span class="params">(users)</span>:</span></span><br><span class="line">    <span class="string">"""Get name of the user in lower case"""</span></span><br><span class="line">    <span class="keyword">return</span> users[<span class="string">"first_name"</span>].lower()</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_sorted_dictionary</span><span class="params">(users)</span>:</span></span><br><span class="line">    <span class="string">"""Sort the nested dictionary"""</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> isinstance(users, dict):</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">"Not a correct dictionary"</span>)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> len(users):</span><br><span class="line">       <span class="keyword">raise</span> ValueError(<span class="string">"Empty dictionary"</span>)</span><br><span class="line">    users_by_name = sorted(users, key=get_user_name)</span><br><span class="line">    <span class="keyword">return</span> users_by_name</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如您所见，此代码检查了所有可能的意外值，并且比起以前的单行代码更具可读性。 单行代码虽然看起来很酷节省了行，但是会给代码添加很多复杂性。 但是这并不意味着单行代码就不好 这里提出的一点是，如果你的单行代码使代码变得更难阅读，那么就请避免使用它，记住写代码不是为了炫酷的，尤其在项目组中。 让我们再考虑一个例子，你试图读取 CSV 文件并计算 CSV 文件处理的行数。下面的代码展示使代码可读的重要性，以及命名如何在使代码可读中发挥重要作用。</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">with</span> open(<span class="string">"employee.csv"</span>, mode=<span class="string">"r"</span>) <span class="keyword">as</span> csv_file:</span><br><span class="line">    csv_reader = csv.DictReader(csv_file)</span><br><span class="line">    line_count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> row <span class="keyword">in</span> csv_reader:</span><br><span class="line">        <span class="keyword">if</span> line_count == <span class="number">0</span>:</span><br><span class="line">            print(<span class="string">f'Column names are <span class="subst">&#123;<span class="string">", "</span>.join(row)&#125;</span>'</span>)</span><br><span class="line">            line_count += <span class="number">1</span></span><br><span class="line">            print(<span class="string">f'\\t<span class="subst">&#123;row[<span class="string">"name"</span>]&#125;</span> salary: <span class="subst">&#123;row[<span class="string">"salary"</span>]&#125;</span>'</span></span><br><span class="line">            <span class="string">f'and was born in <span class="subst">&#123;row[<span class="string">"birthday month"</span>]&#125;</span>.'</span>)</span><br><span class="line">        line_count += <span class="number">1</span></span><br><span class="line">    print(<span class="string">f'Processed <span class="subst">&#123;line_count&#125;</span> lines.'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>将代码分解为函数有助于使复杂的代码变的易于阅读和调试。 这里的代码在 with 语句中执行多项操作。为了提高可读性，您可以将带有 process salary 的代码从 CSV 文件中提取到另一个函数中，以降低出错的可能性。</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">with</span> open(<span class="string">"employee.csv"</span>, mode=<span class="string">"r"</span>) <span class="keyword">as</span> csv_file:</span><br><span class="line">    csv_reader = csv.DictReader(csv_file)</span><br><span class="line">    line_count = <span class="number">0</span></span><br><span class="line">    process_salary(csv_reader)    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">process_salary</span><span class="params">(csv_reader)</span>:</span></span><br><span class="line"><span class="string">"""Process salary of user from csv file."""</span></span><br><span class="line">    <span class="keyword">for</span> row <span class="keyword">in</span> csv_reader:</span><br><span class="line">            <span class="keyword">if</span> line_count == <span class="number">0</span>:</span><br><span class="line">                print(<span class="string">f'Column names are <span class="subst">&#123;<span class="string">", "</span>.join(row)&#125;</span>'</span>)</span><br><span class="line">                line_count += <span class="number">1</span></span><br><span class="line">                print(<span class="string">f'\\t<span class="subst">&#123;row[<span class="string">"name"</span>]&#125;</span> salary: <span class="subst">&#123;row[<span class="string">"salary"</span>]&#125;</span>'</span></span><br><span class="line">                <span class="string">f'and was born in <span class="subst">&#123;row[<span class="string">"birthday month"</span>]&#125;</span>.'</span>)</span><br><span class="line">            line_count += <span class="number">1</span></span><br><span class="line">        print(<span class="string">f'Processed <span class="subst">&#123;line_count&#125;</span> lines.'</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>代码是不是变得容易理解了不少呢。 在这里，创建了一个帮助函数，而不是在with语句中编写所有内容。这使读者清楚地了解了函数的实际作用。如果想处理一个特定的异常或者想从CSV文件中读取更多的数据，可以进一步分解这个函数，以遵循单一职责原则，一个函数一做一件事。这个很重要</p>
                  <h3 id="return语句的类型尽量一致"><a href="#return语句的类型尽量一致" class="headerlink" title="return语句的类型尽量一致"></a><strong>return语句的类型尽量一致</strong></h3>
                  <p>如果希望函数返回一个值，请确保该函数的所有执行路径都返回该值。但是，如果期望函数只是在不返回值的情况下执行操作，则 Python 会隐式返回 None 作为函数的默认值。 先看一个错误示范</p>
                  <figure class="highlight ruby">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">calculate_interest</span><span class="params">(principle, time rate)</span></span>:    </span><br><span class="line">     <span class="keyword">if</span> principle &gt; <span class="number">0</span><span class="symbol">:</span></span><br><span class="line">         <span class="keyword">return</span> (principle * time * rate) / <span class="number">100</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">calculate_interest</span><span class="params">(principle, time rate)</span></span>:    </span><br><span class="line">    <span class="keyword">if</span> principle &lt; <span class="number">0</span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">return</span>    </span><br><span class="line">    <span class="keyword">return</span> (principle * time * rate) / <span class="number">100</span>ChaPTER <span class="number">1</span>  PyThonIC ThInkIng</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>正确的示范应该是下面这样</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">calculate_interest</span><span class="params">(principle, time rate)</span>:</span>    </span><br><span class="line">     <span class="keyword">if</span> principle &gt; <span class="number">0</span>:</span><br><span class="line">         <span class="keyword">return</span> (principle * time * rate) / <span class="number">100</span></span><br><span class="line">     <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">calculate_interest</span><span class="params">(principle, time rate)</span>:</span>    </span><br><span class="line">    <span class="keyword">if</span> principle &lt; <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span>    </span><br><span class="line">    <span class="keyword">return</span> (principle * time * rate) / <span class="number">100</span>ChaPTER <span class="number">1</span>  PyThonIC ThInkIng</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>还是那句话写易读的代码，代码多写点没关系，可读性很重要。</p>
                  <h3 id="使用-isinstance-方法而不是-type-进行比较"><a href="#使用-isinstance-方法而不是-type-进行比较" class="headerlink" title="使用 isinstance() 方法而不是 type() 进行比较"></a><strong>使用 isinstance() 方法而不是 type() 进行比较</strong></h3>
                  <p>当比较两个对象类型时，请考虑使用 isinstance() 而不是 type，因为 isinstance() 判断一个对象是否为另一个对象的子类是 true。考虑这样一个场景：如果传递的数据结构是dict 的子类，比如 orderdict。type() 对于特定类型的数据结构将失败;然而，isinstance() 可以将其识别出它是 dict 的子类。 错误示范</p>
                  <figure class="highlight elm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="title">user_ages</span> = &#123;<span class="string">"Larry"</span>: <span class="number">35</span>, <span class="string">"Jon"</span>: <span class="number">89</span>, <span class="string">"Imli"</span>: <span class="number">12</span>&#125;</span><br><span class="line"><span class="keyword">type</span>(user_ages) == dict:</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>正确选择</p>
                  <figure class="highlight yaml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="string">user_ages</span> <span class="string">=</span> <span class="string">&#123;"Larry":</span> <span class="number">35</span><span class="string">,</span> <span class="attr">"Jon":</span> <span class="number">89</span><span class="string">,</span> <span class="attr">"Imli":</span> <span class="number">12</span><span class="string">&#125;</span></span><br><span class="line"><span class="string">if</span> <span class="string">isinstance(user_ages,</span> <span class="string">dict):</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="比较布尔值"><a href="#比较布尔值" class="headerlink" title="比较布尔值"></a><strong>比较布尔值</strong></h3>
                  <p>在Python中有多种方法可以比较布尔值。 错误示范</p>
                  <figure class="highlight ada">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">if</span> is_empty = <span class="literal">False</span></span><br><span class="line"><span class="keyword">if</span> is_empty == <span class="literal">False</span>:</span><br><span class="line"><span class="keyword">if</span> is_empty <span class="keyword">is</span> <span class="literal">False</span>:</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>正确示范</p>
                  <figure class="highlight ceylon">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">is</span><span class="number">_</span>empty = False</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">is</span><span class="number">_</span>empty</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="使用文档字符串"><a href="#使用文档字符串" class="headerlink" title="使用文档字符串"></a><strong>使用文档字符串</strong></h3>
                  <p>Docstrings可以在 Python 中声明代码的功能的。通常在方法，类和模块的开头使用。 docstring是该对象的<strong>doc</strong>特殊属性。 Python 官方语言建议使用“”三重双引号“”来编写文档字符串。 你可以在 PEP8 官方文档中找到这些实践。 下面让我们简要介绍一下在 Python 代码中编写 docstrings 的一些最佳实践 。</p>
                  <h3 id="方法中使用docstring"><a href="#方法中使用docstring" class="headerlink" title="方法中使用docstring"></a><strong>方法中使用docstring</strong></h3>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_prime_number</span><span class="params">()</span>:</span></span><br><span class="line">    <span class="string">"""Get list of prime numbers between 1 to 100."""</span><span class="string">"</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>关于docstring的格式的写法，目前存在多种风格，但是这几种风格都有一些统一的标准。</p>
                  <ul>
                    <li>即使字符串符合一行，也会使用三重引号。当你想要扩展时，这种注释非常有用。‘</li>
                    <li>三重引号中的字符串前后不应有任何空行</li>
                    <li>使用句点（.）结束docstring中的语句 类似地，可以应用 Python 多行 docstring 规则来编写多行 docstring。在多行上编写文档字符串是用更具描述性的方式记录代码的一种方法。你可以利用 Python 多行文档字符串在 Python 代码中编写描述性文档字符串，而不是在每一行上编写注释。 <strong>多行的docstring</strong></li>
                  </ul>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">call_weather_api</span><span class="params">(url, location)</span>:</span></span><br><span class="line">    <span class="string">"""Get the weather of specific location.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Calling weather api to check for weather by using weather api and </span></span><br><span class="line"><span class="string">    location. Make sure you provide city name only, country and county </span></span><br><span class="line"><span class="string">    names won't be accepted and will throw exception if not found the </span></span><br><span class="line"><span class="string">    city name.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    :param url:URL of the api to get weather.</span></span><br><span class="line"><span class="string">    :type url: str</span></span><br><span class="line"><span class="string">    :param location:Location of the city to get the weather.</span></span><br><span class="line"><span class="string">    :type location: str</span></span><br><span class="line"><span class="string">    :return: Give the weather information of given location.</span></span><br><span class="line"><span class="string">    :rtype: str"""</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>说一下上面代码的注意点</p>
                  <ul>
                    <li>第一行是函数或类的简要描述</li>
                    <li>每一行语句的末尾有一个句号</li>
                    <li>文档字符串中的简要描述和摘要之间有一行空白</li>
                  </ul>
                  <p>如果使用 Python3.6 可以使用类型注解对上面的docstring以及参数的声明进行修改。</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">call_weather_api</span><span class="params">(url: str, location: str)</span> -&gt; str:</span></span><br><span class="line">    <span class="string">"""Get the weather of specific location.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Calling weather api to check for weather by using weather api and </span></span><br><span class="line"><span class="string">    location. Make sure you provide city name only, country and county </span></span><br><span class="line"><span class="string">    names won't be accepted and will throw exception if not found the </span></span><br><span class="line"><span class="string">    city name.</span></span><br><span class="line"><span class="string">    """</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>怎么样是不是简洁了不少，如果使用 Python 代码中的类型注解，则不需要再编写参数信息。 关于类型注解(type hint)的具体用法可以参考我之前写的<a href="https://link.zhihu.com/?target=http%3A//mp.weixin.qq.com/s%3F__biz%3DMzU0NDQ2OTkzNw%3D%3D%26mid%3D2247484258%26idx%3D1%26sn%3Db13db84dad8eccaec9cd4af618e812e4%26chksm%3Dfb7ae5bccc0d6caa8e145e9fd22d0395e68d618672ebbfe99794926c0b9e782974fb16321e32%26scene%3D21%23wechat_redirect">http://mp.weixin.qq.com/s?__biz=MzU0NDQ2OTkzNw==&amp;mid=2247484258&amp;idx=1&amp;sn=b13db84dad8eccaec9cd4af618e812e4&amp;chksm=fb7ae5bccc0d6caa8e145e9fd22d0395e68d618672ebbfe99794926c0b9e782974fb16321e32&amp;scene=21#wechat_redirect</a></p>
                  <h3 id="模块级别的docstring"><a href="#模块级别的docstring" class="headerlink" title="模块级别的docstring"></a><strong>模块级别的docstring</strong></h3>
                  <p>一般在文件的顶部放置一个模块级的 docstring 来简要描述模块的使用。 这些注释应该放在在导包之前，模块文档字符串应该表明模块的使用方法和功能。 如果觉得在使用模块之前客户端需要明确地知道方法或类，你还可以简要地指定特定方法或类。</p>
                  <figure class="highlight sql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">"""This module contains all of the network related requests. </span><br><span class="line">This module will <span class="keyword">check</span> <span class="keyword">for</span> <span class="keyword">all</span> the <span class="keyword">exceptions</span> <span class="keyword">while</span> making the network </span><br><span class="line">calls <span class="keyword">and</span> <span class="keyword">raise</span> <span class="keyword">exceptions</span> <span class="keyword">for</span> <span class="keyword">any</span> <span class="literal">unknown</span> exception.</span><br><span class="line">Make sure that <span class="keyword">when</span> you <span class="keyword">use</span> this <span class="keyword">module</span>,</span><br><span class="line">you handle these <span class="keyword">exceptions</span> <span class="keyword">in</span> <span class="keyword">client</span> code <span class="keyword">as</span>:</span><br><span class="line">NetworkError <span class="keyword">exception</span> <span class="keyword">for</span> network calls.</span><br><span class="line">NetworkNotFound <span class="keyword">exception</span> <span class="keyword">if</span> network <span class="keyword">not</span> found.</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">import urllib3</span></span><br><span class="line"><span class="string">import json</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>在为模块编写文档字符串时，应考虑执行以下操作：</p>
                  <ul>
                    <li>对当前模块写一个简要的说明</li>
                    <li>如果想指定某些对读者有用的模块，如上面的代码，还可以添加异常信息，但是注意不要太详细。</li>
                  </ul>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">NetworkError exception <span class="keyword">for</span><span class="built_in"> network </span>calls.</span><br><span class="line">NetworkNotFound exception <span class="keyword">if</span><span class="built_in"> network </span><span class="keyword">not</span> found.</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <ul>
                    <li>将模块的docstring看作是提供关于模块的描述性信息的一种方法，而不需要详细讨论每个函数或类具体操作方法。 <strong>类级别的docstring</strong> 类docstring主要用于简要描述类的使用及其总体目标。 让我们看一些示例，看看如何编写类文档字符串 <strong>单行类docstring</strong></li>
                  </ul>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Student</span>:</span></span><br><span class="line">   <span class="string">"""This class handle actions performed by a student."""</span></span><br><span class="line">   <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">      <span class="keyword">pass</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这个类有一个一行的 docstring，它简要地讨论了学生类。如前所述，遵守了所以一行docstring 的编码规范。</p>
                  <h3 id="多行类docstring"><a href="#多行类docstring" class="headerlink" title="多行类docstring"></a><strong>多行类docstring</strong></h3>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Student</span>:</span></span><br><span class="line">    <span class="string">"""Student class information.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    This class handle actions performed by a student.</span></span><br><span class="line"><span class="string">    This class provides information about student full name, age, </span></span><br><span class="line"><span class="string">    roll-number and other information.</span></span><br><span class="line"><span class="string">    Usage:</span></span><br><span class="line"><span class="string">        import student</span></span><br><span class="line"><span class="string">        student = student.Student()</span></span><br><span class="line"><span class="string">        student.get_name()</span></span><br><span class="line"><span class="string">        &gt;&gt;&gt; 678998</span></span><br><span class="line"><span class="string">   """</span></span><br><span class="line">   <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">       <span class="keyword">pass</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这个类 docstring 是多行的; 我们写了很多关于 Student 类的用法以及如何使用它。</p>
                  <h3 id="函数的docstring"><a href="#函数的docstring" class="headerlink" title="函数的docstring"></a><strong>函数的docstring</strong></h3>
                  <p>函数文档字符串可以写在函数之后，也可以写在函数的顶部。</p>
                  <figure class="highlight sql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">def is_prime_number(number):</span><br><span class="line">    """<span class="keyword">Check</span> <span class="keyword">for</span> prime number.</span><br><span class="line"></span><br><span class="line">    <span class="keyword">Check</span> the given <span class="built_in">number</span> <span class="keyword">is</span> prime <span class="built_in">number</span> </span><br><span class="line">    <span class="keyword">or</span> <span class="keyword">not</span> <span class="keyword">by</span> checking against <span class="keyword">all</span> the numbers </span><br><span class="line">    <span class="keyword">less</span> the <span class="keyword">square</span> root <span class="keyword">of</span> given number.</span><br><span class="line"></span><br><span class="line">    :param <span class="built_in">number</span>:Given <span class="built_in">number</span> <span class="keyword">to</span> <span class="keyword">check</span> <span class="keyword">for</span> prime</span><br><span class="line">    :<span class="keyword">type</span> <span class="built_in">number</span>: <span class="built_in">int</span></span><br><span class="line">    :<span class="keyword">return</span>: <span class="literal">True</span> <span class="keyword">if</span> <span class="built_in">number</span> <span class="keyword">is</span> prime otherwise False.</span><br><span class="line">    :rtype: <span class="built_in">boolean</span></span><br><span class="line">    <span class="string">"""</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果我们使用类型注解对其进一步优化。</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">is_prime_number</span><span class="params">(number: int)</span>-&gt;bool:</span></span><br><span class="line">    <span class="string">"""Check for prime number.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Check the given number is prime number </span></span><br><span class="line"><span class="string">    or not by checking against all the numbers </span></span><br><span class="line"><span class="string">    less the square root of given number.</span></span><br><span class="line"><span class="string">    """</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="结语"><a href="#结语" class="headerlink" title="结语"></a><strong>结语</strong></h3>
                  <p>当然关于 Python 中的规范还有很多很多，建议大家参考 Python 之禅和 Pep8 对代码进行优化，养成编写 Pythonic 代码的良好习惯。 [caption id=”attachment_6600” align=”alignnone” width=”258”]<img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/06/qrcode_for_gh_fedbc9cddff8_258.jpg" alt=""> 扫码关注[/caption] 更多精彩内容关注微信公众号:python学习开发</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/陈祥安" class="author" itemprop="url" rel="index">陈祥安</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-06-13 15:00:05" itemprop="dateCreated datePublished" datetime="2019-06-13T15:00:05+08:00">2019-06-13</time>
                </span>
                <span id="/6607.html" class="post-meta-item leancloud_visitors" data-flag-title="代码整洁之道-编写 Pythonic 代码" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>8.6k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>8 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6598.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6598.html" class="post-title-link" itemprop="url">Python3 中使用 Pathlib 模块进行文件操作</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>在本教程中，你将了解如何使用 pathlib 模块操作目录和文件的名称。 学习如何读取和写入文件，拼接路径和操作底层文件系统的新方法，以及如何列出文件并迭代它们的一些示例。 大多人处理文件用的最多的还是 os 模快吧，比如下面这样的操作</p>
                  <figure class="highlight ruby">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="meta">&gt;&gt;</span>&gt; path.rsplit(<span class="string">'\\'</span>, maxsplit=<span class="number">1</span>)[<span class="number">0</span>]</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>或者写出下面这样长长的代码</p>
                  <figure class="highlight lua">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&gt;&gt;&gt; <span class="built_in">os</span>.<span class="built_in">path</span>.isfile(<span class="built_in">os</span>.<span class="built_in">path</span>.join(<span class="built_in">os</span>.<span class="built_in">path</span>.expanduser(<span class="string">'~'</span>), <span class="string">'realpython.txt'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>使用 pathlib 模块，可以使代码使用优雅，可读和 Pythonic 代码重写上面的两个示例，如：</p>
                  <figure class="highlight ruby">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="meta">&gt;&gt;</span>&gt; path.parent</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; (pathlib.Path.home() / <span class="string">'realpython.txt'</span>).is_file()</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="Python-文件路径处理问题"><a href="#Python-文件路径处理问题" class="headerlink" title="Python 文件路径处理问题"></a><strong>Python 文件路径处理问题</strong></h3>
                  <p>由于许多不同的原因，使用文件和与文件系统交互很重要。 最简单的情况可能只涉及读取或写入文件，但有时候会有更复杂的任务。 也许你需要列出给定类型的目录中的所有文件，查找给定文件的父目录，或者创建一个尚不存在的唯一文件名。 一般情况，Python 使用常规文本字符串表示文件路径。 一般在使用 os,glob 和 shutil 等库的时候会使用到路径拼接的操作，使用os模块拼接起来显得略显复杂，以下示例仅需要三个 import 语句来将所有文本文件移动到归档目录：</p>
                  <figure class="highlight haxe">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> glob</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> shutil</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> file_name <span class="keyword">in</span> glob.glob(<span class="string">'*.txt'</span>):<span class="type"></span></span><br><span class="line"><span class="type">    new_path </span>= os.path.join(<span class="string">'archive'</span>, file_name)</span><br><span class="line">    shutil.move(file_name, <span class="keyword">new</span><span class="type">_path</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>使用常规的字符串去拼接路径是可以的，但是由于不同的操作系统使用的分隔符不同，这样就容易出现问题，所以一般我们使用最多的还是使用 os.path.join()。 Python 3.4 中引入了 pathlib 模块（PEP 428）再一次的优化了路径的拼接。使用 pathlib 库的 Path 方法，可以将一个普通的字符串转换为 pathlib.Path 对象类型的路径。 早期，其他软件包仍然使用字符串作为文件路径，但从 Python 3.6 开始，pathlib 模块在整个标准库中得到支持，部分原因是由于增加了文件系统路径协议。 如果你坚持使用传统的 Python，那么 Python 2 也有一个可用的向后移植。 ok，说了那么多下面让我们看看 pathlib 如何在实践中发挥作用。</p>
                  <h3 id="创建路径"><a href="#创建路径" class="headerlink" title="创建路径"></a><strong>创建路径</strong></h3>
                  <p>这里我们首先要知道两个用法，先看代码:</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> <span class="type">Path</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>你真正需要知道的是 pathlib.Path 类。 创建路径有几种不同的方式。 首先，有类方法，如 .cwd（当前工作目录）和 .home（用户的主目录）：</p>
                  <figure class="highlight elm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="title">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="title">now_path</span> = <span class="type">Path</span>.cwd()</span><br><span class="line"><span class="title">home_path</span> = <span class="type">Path</span>.home()</span><br><span class="line"></span><br><span class="line"><span class="title">print</span>(<span class="string">"当前工作目录"</span>,now_path,<span class="keyword">type</span>(now_path))</span><br><span class="line"><span class="title">print</span>(<span class="string">"home目录"</span>,home_path,<span class="keyword">type</span>(home_path))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>输出内容</p>
                  <figure class="highlight gradle">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">当前工作目录 <span class="regexp">/Users/</span>chennan<span class="regexp">/pythonproject/</span>demo &lt;<span class="keyword">class</span> <span class="string">'pathlib.PosixPath'</span>&gt;</span><br><span class="line">home目录 <span class="regexp">/Users/</span>chennan &lt;<span class="keyword">class</span> <span class="string">'pathlib.PosixPath'</span>&gt;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以发现路径格式为 pathlib.PosixPath 这是在 unix 系统下的显示。在不同的系统上显示的格式也是不一样，在 windows 系统会显示为 WindowsPath。但是不管什么显示类型，都不影响后面的操作。 前面我们提到过可以通过把字符串类型的路径，转换为 Pathlib.Path 类型的路径，经过测试发现在 Python3.4 以后很多模块以及支持该格式的路径。不用转为成字符串使用了。比起 os.path.join 拼接路径的方式, pathlib 使用起来更加的方便,使用示例如下:</p>
                  <figure class="highlight elm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> pathlib</span><br><span class="line"><span class="type">DIR_PATH</span> = pathlib.<span class="type">Path</span>(<span class="string">"/Users/chennan/CDM"</span>)</span><br><span class="line"><span class="title">print</span>(<span class="type">DIR_PATH</span>,<span class="keyword">type</span>(<span class="type">DIR_PATH</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>输出内容:</p>
                  <figure class="highlight ceylon">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">/Users/chennan/CDM &lt;<span class="keyword">class</span> <span class="string">'pathlib.PosixPath'</span>&gt;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>通过 “/“ 我们就可以对路径进行拼接了，怎么样是不是很方便呢。</p>
                  <h3 id="读文件和写文件"><a href="#读文件和写文件" class="headerlink" title="读文件和写文件"></a><strong>读文件和写文件</strong></h3>
                  <p>在我们使用 open 来操作文件读写操作的时候,不仅可以使用字符串格式的路径，对于 pathlib 生成的路径完全可以直接使用:</p>
                  <figure class="highlight vim">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">path = pathlib.Path.cwd() / <span class="string">'test.md'</span></span><br><span class="line">with <span class="keyword">open</span>(path, <span class="keyword">mode</span>=<span class="string">'r'</span>) <span class="keyword">as</span> fid:</span><br><span class="line">    headers = [<span class="built_in">line</span>.strip() <span class="keyword">for</span> <span class="built_in">line</span> in fid <span class="keyword">if</span> <span class="built_in">line</span>.startswith(<span class="string">'#'</span>)]</span><br><span class="line"><span class="keyword">print</span>(<span class="string">'\n'</span>.<span class="keyword">join</span>(headers))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>或者在 pathlib 的基础使用 open,我们推荐使用下面的方式</p>
                  <figure class="highlight haskell">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> pathlib</span><br><span class="line"><span class="type">DIR_PATH</span> = pathlib.<span class="type">Path</span>(<span class="string">"/Users/chennan/CDM"</span>) / <span class="string">"2000"</span> / <span class="string">"hehe.txt"</span></span><br><span class="line"><span class="title">with</span> <span class="type">DIR_PATH</span>.open(<span class="string">"r"</span>) <span class="keyword">as</span> fs:</span><br><span class="line">     <span class="class"><span class="keyword">data</span> = fs.read() </span></span><br><span class="line"><span class="title">print</span>(<span class="class"><span class="keyword">data</span>)</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样写的好处就是 open 里面我们不需要再去传入路径了，直接指定文件读写模式即可。实际上这里的 open 方法，底层也是调用了 os.open 的方法。使用哪种方式看个人的喜好。 pathlib 还提供几种文件的读写方式: 可以不用再使用 with open 的形式即可以进行读写。</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">.read<span class="constructor">_text()</span>: 找到对应的路径然后打开文件，读成str格式。等同<span class="keyword">open</span>操作文件的<span class="string">"r"</span>格式。</span><br><span class="line">.read<span class="constructor">_bytes()</span>: 读取字节流的方式。等同<span class="keyword">open</span>操作文件的<span class="string">"rb"</span>格式。</span><br><span class="line">.write<span class="constructor">_text()</span>: 文件的写的操作，等同<span class="keyword">open</span>操作文件的<span class="string">"w"</span>格式。</span><br><span class="line">.write<span class="constructor">_bytes()</span>: 文件的写的操作，等同<span class="keyword">open</span>操作文件的<span class="string">"wb"</span>格式。</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>使用 resolve 可以通过传入文件名，来返回文件的完整路径，使用方式如下</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import pathlib</span><br><span class="line">py_path =pathlib.Path(<span class="string">"superdemo.py"</span>)</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(py_path.resolve()</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>输出</p>
                  <figure class="highlight awk">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="regexp">/Users/</span>chennan<span class="regexp">/pythonproject/</span>demo<span class="regexp">/superdemo.py</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>需要注意的是 “superdemo.py” 文件要和我当前的程序文件在同一级目录。</p>
                  <h3 id="选择路径的不同组成部分"><a href="#选择路径的不同组成部分" class="headerlink" title="选择路径的不同组成部分"></a><strong>选择路径的不同组成部分</strong></h3>
                  <p>pathlib 还提供了很多路径操作的属性，这些属性可以选择路径的不用部位，如 <strong>.name</strong>: 可以获取文件的名字，包含拓展名。 <strong>.parent</strong>: 返回上级文件夹的名字 <strong>.stem</strong>: 获取文件名不包含拓展名 <strong>.suffix</strong>: 获取文件的拓展名 <strong>.anchor</strong>: 类似盘符的一个东西,</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import pathlib</span><br><span class="line"></span><br><span class="line">now_path = pathlib<span class="selector-class">.Path</span>.cwd() / <span class="string">"demo.txt"</span></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(<span class="string">"name"</span>,now_path.name)</span></span></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(<span class="string">"stem"</span>,now_path.stem)</span></span></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(<span class="string">"suffix"</span>,now_path.suffix)</span></span></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(<span class="string">"parent"</span>,now_path.parent)</span></span></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(<span class="string">"anchor"</span>,now_path.anchor)</span></span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>输出内容如下</p>
                  <figure class="highlight pf">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">name demo.txt</span><br><span class="line">stem demo</span><br><span class="line">suffix .txt</span><br><span class="line">parent /Users/chennan/pythonproject/demo</span><br><span class="line"><span class="built_in">anchor</span> /</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="移动和删除文件"><a href="#移动和删除文件" class="headerlink" title="移动和删除文件"></a><strong>移动和删除文件</strong></h3>
                  <p>当然 pathlib 还可以支持文件其他操作，像移动，更新，甚至删除文件，但是使用这些方法的时候要小心因为，使用过程不用有任何的错误提示即使文件不存在也不会出现等待的情况。 使用 replace 方法可以移动文件，如果文件存在则会覆盖。为避免文件可能被覆盖，最简单的方法是在替换之前测试目标是否存在。</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> pathlib</span><br><span class="line"></span><br><span class="line">destination = pathlib.Path.cwd() / "target" </span><br><span class="line">source = pathlib.Path.cwd() / "demo.txt"</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> destination.<span class="keyword">exists</span>():</span><br><span class="line">    source.replace(destination)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>但是上面的方法存在问题就是，在多个进程多 destination 进行的操作的时候就会现问题，可以使用下面的方法避免这个问题。也就是说上面的方法适合单个文件的操作。</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import pathlib</span><br><span class="line"></span><br><span class="line">destination = pathlib.<span class="module-access"><span class="module"><span class="identifier">Path</span>.</span></span>cwd<span class="literal">()</span><span class="operator"> / </span><span class="string">"target"</span> </span><br><span class="line">source = pathlib.<span class="module-access"><span class="module"><span class="identifier">Path</span>.</span></span>cwd<span class="literal">()</span><span class="operator"> / </span><span class="string">"demo.txt"</span></span><br><span class="line"><span class="keyword">with</span> destination.<span class="keyword">open</span>(mode='xb') <span class="keyword">as</span> fid: </span><br><span class="line">    #xb表示文件不存在才操作</span><br><span class="line">    fid.write(source.read<span class="constructor">_bytes()</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>当 destination文件存在的时候上面的代码就会出现 FileExistsError 异常。 从技术上讲，这会复制一个文件。 要执行移动，只需在复制完成后删除源即可。 使用 with_name 和 with.shuffix 可以修改文件名字或者后缀。</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import pathlib</span><br><span class="line">source = pathlib.<span class="module-access"><span class="module"><span class="identifier">Path</span>.</span></span>cwd<span class="literal">()</span><span class="operator"> / </span><span class="string">"demo.py"</span></span><br><span class="line">source.replace(source.<span class="keyword">with</span><span class="constructor">_suffix(<span class="string">".txt"</span>)</span>) #修改后缀并移动文件，即重命名</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以使用 .rmdir() 和 .unlink() 来删除文件。</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import pathlib</span><br><span class="line"></span><br><span class="line">destination = pathlib.<span class="module-access"><span class="module"><span class="identifier">Path</span>.</span></span>cwd<span class="literal">()</span><span class="operator"> / </span><span class="string">"target"</span> </span><br><span class="line">source = pathlib.<span class="module-access"><span class="module"><span class="identifier">Path</span>.</span></span>cwd<span class="literal">()</span><span class="operator"> / </span><span class="string">"demo.txt"</span></span><br><span class="line">source.unlink<span class="literal">()</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="几个-pathlib-的使用例子"><a href="#几个-pathlib-的使用例子" class="headerlink" title="几个 pathlib 的使用例子"></a><strong>几个 pathlib 的使用例子</strong></h3>
                  <h3 id="统计文件个数"><a href="#统计文件个数" class="headerlink" title="统计文件个数"></a><strong>统计文件个数</strong></h3>
                  <p>我们可以使用.iterdir方法获取当前文件下的所以文件.</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import pathlib</span><br><span class="line">from collections import Counter</span><br><span class="line">now_path = pathlib<span class="selector-class">.Path</span>.cwd()</span><br><span class="line">gen = (<span class="selector-tag">i</span><span class="selector-class">.suffix</span> <span class="keyword">for</span> <span class="selector-tag">i</span> <span class="keyword">in</span> now_path.iterdir())</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(Counter(gen)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>输出内容</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="title">Counter</span><span class="params">(&#123;<span class="string">'.py'</span>: <span class="number">16</span>, <span class="string">''</span>: <span class="number">11</span>, <span class="string">'.txt'</span>: <span class="number">1</span>, <span class="string">'.png'</span>: <span class="number">1</span>, <span class="string">'.csv'</span>: <span class="number">1</span>&#125;)</span></span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>通过配合使用 collections 模块的 Counter 方法，我们获取了当文件夹下文件类型情况。 前面我们说过 glob 模块点这里了解【<a href="https://link.zhihu.com/?target=https%3A//www.cnblogs.com/c-x-a/p/9261832.html">https://www.cnblogs.com/c-x-a/p/9261832.html</a>】，同样的 pathlib 也有 glob 方法和 rglob 方法，不同的是 glob 模块里的 glob 方法结果是列表形式的，iglob 是生成器类型，在这里 pathlib 的 glob 模块返回的是生成器类型，然后 pathlib 还有一个支持递归操作的 rglob 方法。 下面的这个操作我通过使用 glob 方法，设定规则进行文件的匹配。</p>
                  <figure class="highlight lisp">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import pathlib</span><br><span class="line">from  collections import Counter</span><br><span class="line">gen =(<span class="name">p</span>.suffix for p in pathlib.Path.cwd().glob('*.py'))</span><br><span class="line">print(<span class="name">Counter</span>(<span class="name">gen</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="展示目录树"><a href="#展示目录树" class="headerlink" title="展示目录树"></a><strong>展示目录树</strong></h3>
                  <p>下一个示例定义了一个函数 tree()，该函数的作用是打印一个表示文件层次结构的可视树，该树以一个给定目录为根。因为想列出其子目录，所以我们要使用 .rglob() 方法：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> pathlib</span><br><span class="line"><span class="keyword">from</span>  collections <span class="keyword">import</span> Counter</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">tree</span><span class="params">(directory)</span>:</span></span><br><span class="line">    print(<span class="string">f'+ <span class="subst">&#123;directory&#125;</span>'</span>)</span><br><span class="line">    <span class="keyword">for</span> path <span class="keyword">in</span> sorted(directory.rglob(<span class="string">'*'</span>)):</span><br><span class="line">        depth = len(path.relative_to(directory).parts)</span><br><span class="line">        spacer = <span class="string">'    '</span> * depth</span><br><span class="line">        print(<span class="string">f'<span class="subst">&#123;spacer&#125;</span>+ <span class="subst">&#123;path.name&#125;</span>'</span>)</span><br><span class="line"></span><br><span class="line">now_path = pathlib.Path.cwd()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    tree(now_path)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>其中 relative_to 的方法的作用是返回 path 相对于 directory 的路径。 parts 方法可以返回路径的各部分。例如</p>
                  <figure class="highlight coffeescript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> pathlib</span><br><span class="line">now_path = pathlib.Path.cwd()</span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    <span class="built_in">print</span>(now_path.parts)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>返回</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">(<span class="symbol">'/</span>', <span class="symbol">'Users</span>', <span class="symbol">'chennan</span>', <span class="symbol">'pythonproject</span>', <span class="symbol">'demo</span>')</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="获取文件最后一次修改时间"><a href="#获取文件最后一次修改时间" class="headerlink" title="获取文件最后一次修改时间"></a><strong>获取文件最后一次修改时间</strong></h3>
                  <p>iterdir(),.glob()和.rglob()方法非常适合于生成器表达式和列表理解。 使用stat()方法可以获取文件的一些基本信息，使用.stat().st_mtime可以获取文件最后一次修改的信息</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> pathlib</span><br><span class="line">now_path = pathlib.Path.cwd()</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="type">time</span>, file_path = max((f.stat().st_mtime, f) <span class="keyword">for</span> f <span class="keyword">in</span> now_path.iterdir())</span><br><span class="line">print(datetime.fromtimestamp(<span class="type">time</span>), file_path)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>甚至可以使用类似的表达式获取上次修改的文件内容</p>
                  <figure class="highlight swift">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> pathlib</span><br><span class="line">from datetime <span class="keyword">import</span> datetime</span><br><span class="line">now_path =pathlib.<span class="type">Path</span>.cwd()</span><br><span class="line">result = <span class="built_in">max</span>((f.stat().st_mtime, f) <span class="keyword">for</span> f <span class="keyword">in</span> now_path.iterdir())[<span class="number">1</span>]</span><br><span class="line"><span class="built_in">print</span>(result.read_text())</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>.stat().st_mtime 会返回文件的时间戳，可以使用 datetime 或者 time 模块对时间格式进行进一步转换。</p>
                  <h3 id="其他内容"><a href="#其他内容" class="headerlink" title="其他内容"></a><strong>其他内容</strong></h3>
                  <h3 id="关于-pathlib-Path-格式路径转换为字符串类型"><a href="#关于-pathlib-Path-格式路径转换为字符串类型" class="headerlink" title="关于 pathlib.Path 格式路径转换为字符串类型"></a><strong>关于 pathlib.Path 格式路径转换为字符串类型</strong></h3>
                  <p>因为通过 pathlib 模块操作生成的路径，不能直接应用字符串的一些操作，所以需要转换成字符串，虽然可以使用 str() 函数进行转换，但是安全性不高，建议使用 os.fspath() 方法，因为如果路径格式非法的，可以抛出一个异常。str()就不能做到这一点。</p>
                  <h3 id="拼接符号”-“背后的秘密"><a href="#拼接符号”-“背后的秘密" class="headerlink" title="拼接符号”/“背后的秘密"></a><strong>拼接符号”/“背后的秘密</strong></h3>
                  <p>/ 运算符由 <strong>truediv</strong> 方法定义。 实际上，如果你看一下 pathlib 的源代码，你会看到类似的东西。</p>
                  <figure class="highlight ruby">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PurePath</span>(<span class="title">object</span>):</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__truediv__</span><span class="params">(<span class="keyword">self</span>, key)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">self</span>._make_child((key,))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h3 id="后记"><a href="#后记" class="headerlink" title="后记"></a><strong>后记</strong></h3>
                  <p>从 Python 3.4 开始，pathlib 已在标准库中提供。 使用 pathlib，文件路径可以由适当的 Path 对象表示，而不是像以前一样用纯字符串表示。 这些对象使代码处理文件路径：</p>
                  <ul>
                    <li>更容易阅读，特别是可以使用“/”将路径连接在一起</li>
                    <li>更强大，直接在对象上提供最必要的方法和属性</li>
                    <li>在操作系统中更加一致，因为Path对象隐藏了不同系统的特性</li>
                  </ul>
                  <p>在本教程中，你已经了解了如何创建 Path 对象、读取和写入文件、操作路径和底层文件系统，以及如何遍历多个文件路径等一系列实例。 最后，建议下去自己多加练习，我对文章中的代码都进行了验证，不会出现运行错误的情况。 ————————————————————————————————————————————— 原文: <a href="https://realpython.com/python-pathlib/" target="_blank" rel="noopener">https://realpython.com/python-pathlib/</a> 译者: 陈祥安 [gallery ids=”6600”] 更多精彩内容，请关注微信公众号: python学习开发。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/陈祥安" class="author" itemprop="url" rel="index">陈祥安</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-06-13 14:48:34" itemprop="dateCreated datePublished" datetime="2019-06-13T14:48:34+08:00">2019-06-13</time>
                </span>
                <span id="/6598.html" class="post-meta-item leancloud_visitors" data-flag-title="Python3 中使用 Pathlib 模块进行文件操作" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>6.6k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>6 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6533.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> JavaScript <i class="label-arrow"></i>
                  </a>
                  <a href="/6533.html" class="post-title-link" itemprop="url">中英文排版空格问题解决方案</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>想写这篇文章很久了，也想做这件事很久了，我个人感觉自己是有强迫症的，所以一直有什么事让我看着不太舒服就想把它纠正过来。 文字，也不例外。 现在大家看各种新闻啊、文章啊，几乎每篇文章都会有点数字和英文的吧，比如就拿 Python 来说，看下面两句话：</p>
                  <ul>
                    <li>卧槽Python真牛逼啊排名第1了。</li>
                    <li>卧槽 Python 真牛逼啊排名第 1 了。</li>
                  </ul>
                  <p>Python 是不是第一先不说，就看看上面两句话的排版，哪个看起来更舒服？说实话我是真觉得第一句话太别扭了。因为我们大部分的文本编辑器和浏览器是没有对中文和外文的混排做排版优化的，所以如果写的时候如果二者之间不加个空格，二者就会紧紧贴在一起，然后就变成了上面第一句的样子。 当然如果你觉得第一句的排版更好看，好吧，那么本文后面的内容其实可以不必看了。OK，如果你觉得第二个好看，那不妨接着看下去哈。</p>
                  <h2 id="出发点"><a href="#出发点" class="headerlink" title="出发点"></a>出发点</h2>
                  <p>首先有一点需要明确的是，中英文排版的美学是在于 Readability，易读性。而为了易读性，中英文之间是需要留有”间距”的，注意这里是间距，不是说的”空格”。”空格”会造成间距，但是间距不一定非得需要”空格”。 好，所以，其实我们只需要留有适当的间距，就会显得美观易读，这个间距大约是一个半角空格的距离。 好明确了这一点，我们只要能留有间距，不一定非得加空格。 现在很多专业的排版软件，比如 Adobe InDesign、Microsoft Word 对中英文混排支持非常好，他们会有这么一个功能：可以设置中文西文之间留适当的间距。 所以，如果如果我们使用了这些软件，本身就可以做到 Readability，这就够了。 但是，为什么还会说空格的问题呢？这是因为现在绝大多数软件，不管是文本编辑器还是网页，都没有这个机制。 几乎所有的文本编辑器和浏览器中，只要我们中文和英文连续输入，它们之间是不会出现间距的，就像文章开头所示的样例中的第一句话，显得很别扭。但比如 Adobe InDesign、Microsoft Word、IE 浏览器会有这方面的支持。 所以，怎么解决？手动加空格。 因此，总结下：</p>
                  <ul>
                    <li>间距要有，但不一定是空格。</li>
                    <li>部分软件能自动呈现间距，那就不必加空格。</li>
                    <li>绝大多数软件不能自动呈现间距，那就需要手动加空格。</li>
                  </ul>
                  <p>所以，作为强迫症的我，一定是会为了这个间距而去敲下一个空格的。 <strong>「有研究显示，打字的时候不喜欢在中文和英文之间加空格的人，感情路都走得很辛苦，有七成的比例会在 34 岁的时候跟自己不爱的人结婚，而其余三成的人最后只能把遗产留给自己的猫。毕竟爱情跟书写都需要适时地留白。与大家共勉之。」</strong> <img src="https://qiniu.cuiqingcai.com/2019-05-07-165210.jpg" alt="盘古之白"> 所以，求求你加个空格吧（逃。</p>
                  <h2 id="规范"><a href="#规范" class="headerlink" title="规范"></a>规范</h2>
                  <p>好，下面就说到规范的问题了，到底什么时候应该加空格什么时候不加，这也是有讲究的。下面的内容摘自 GitHub 上的一个中英文混排规范，网址为：<a href="https://github.com/mzlogin/chinese-copywriting-guidelines" target="_blank" rel="noopener">https://github.com/mzlogin/chinese-copywriting-guidelines</a>，下面转述一下。</p>
                  <h3 id="1-中英文之间需要增加空格"><a href="#1-中英文之间需要增加空格" class="headerlink" title="1. 中英文之间需要增加空格"></a>1. 中英文之间需要增加空格</h3>
                  <p>中英文之间是需要添加空格的，不论是普通英文还是引用的英文，下面给个示例： 正确：</p>
                  <ul>
                    <li>在 LeanCloud 上，数据存储是围绕 <code>AVObject</code> 进行的。</li>
                  </ul>
                  <p>错误：</p>
                  <ul>
                    <li>在LeanCloud上，数据存储是围绕<code>AVObject</code>进行的。</li>
                    <li>在 LeanCloud上，数据存储是围绕<code>AVObject</code> 进行的。</li>
                  </ul>
                  <p>完整的正确用法：</p>
                  <ul>
                    <li>在 LeanCloud 上，数据存储是围绕 <code>AVObject</code> 进行的。每个 <code>AVObject</code> 都包含了与 JSON 兼容的 key-value 对应的数据。数据是 schema-free 的，你不需要在每个 <code>AVObject</code> 上提前指定存在哪些键，只要直接设定对应的 key-value 即可。</li>
                  </ul>
                  <p>但有例外，比如「豆瓣FM」等产品名词，按照官方所定义的格式书写。 再比如，我的公众号为「进击的Coder」，那么这里面就不要加空格，按照其本身的形式书写即可。</p>
                  <h3 id="中文与数字之间需要增加空格"><a href="#中文与数字之间需要增加空格" class="headerlink" title="中文与数字之间需要增加空格"></a>中文与数字之间需要增加空格</h3>
                  <p>中文和数字之间也是需要的，下面给个示例： 正确：</p>
                  <ul>
                    <li>今天出去买菜花了 5000 元。</li>
                  </ul>
                  <p>错误：</p>
                  <ul>
                    <li>今天出去买菜花了 5000元。</li>
                    <li>今天出去买菜花了5000元。</li>
                  </ul>
                  <h3 id="数字与单位之间无需增加空格"><a href="#数字与单位之间无需增加空格" class="headerlink" title="数字与单位之间无需增加空格"></a>数字与单位之间无需增加空格</h3>
                  <p>但是数字和单位之间不需要再加额外的空格了，下面给个 正确：</p>
                  <ul>
                    <li>我家的光纤入户宽带有 10Gbps，SSD 一共有 10TB。</li>
                  </ul>
                  <p>错误：</p>
                  <ul>
                    <li>我家的光纤入户宽带有 10 Gbps，SSD 一共有 20 TB。</li>
                  </ul>
                  <p>另外，度／百分比与数字之间不需要增加空格： 正确：</p>
                  <ul>
                    <li>今天是 233° 的高温。</li>
                    <li>新 MacBook Pro 有 15% 的 CPU 性能提升。</li>
                  </ul>
                  <p>错误：</p>
                  <ul>
                    <li>今天是 233 ° 的高温。</li>
                    <li>新 MacBook Pro 有 15 % 的 CPU 性能提升。</li>
                  </ul>
                  <h3 id="全角标点与其他字符之间不加空格"><a href="#全角标点与其他字符之间不加空格" class="headerlink" title="全角标点与其他字符之间不加空格"></a>全角标点与其他字符之间不加空格</h3>
                  <p>标点是分全角和半角的，全角标点一般是在中文状态下输出来的，比如 <code>，</code>、<code>。</code>、<code>！</code>，半角标点一般是在英文状态下输出来的，比如 <code>,</code>、<code>.</code>、<code>!</code>，两个看起来不一样吧？所以，如果是中文标点，即全角标点，那不需要加空格。 正确：</p>
                  <ul>
                    <li>刚刚买了一部 iPhone，好开心！</li>
                  </ul>
                  <p>错误：</p>
                  <ul>
                    <li>刚刚买了一部 iPhone ，好开心！</li>
                  </ul>
                  <p>嗯，基本就是以上的几个规范，只要明白了这些规范，中英文混排就 OK 了！</p>
                  <h2 id="网页"><a href="#网页" class="headerlink" title="网页"></a>网页</h2>
                  <p>有人说，我就是不想打空格，在网页中，我能像 Microsoft Word 一样不打空格而直接显示间距吗？ 也就是说，我能不能设置一个 CSS 样式，就能使得中英文之间自动留有间距呢？ 其实，只有 IE 有这样的支持。这个 CSS 样式叫做 <code>\-ms-text-autospace</code> ，可以在这里了解下：<a href="https://msdn.microsoft.com/library/ms531164(v=vs.85" target="_blank" rel="noopener">https://msdn.microsoft.com/library/ms531164(v=vs.85).aspx</a>.aspx)。 但是很遗憾的是，几乎所有其他的浏览器都不支持这个，Chrome、Firefox 统统都不支持这个特性。放弃吧。 <img src="https://qiniu.cuiqingcai.com/2019-05-07-165207.png" alt="image-20190507220822252"> 这里提供一些手动的解决方案，比如使用 JavaScript 添加标记，然后 CSS 控制标记的间距，解决方案可以参考：<a href="http://mastermay.github.io/text-autospace.js/" target="_blank" rel="noopener">http://mastermay.github.io/text-autospace.js/</a>。</p>
                  <h2 id="编辑器"><a href="#编辑器" class="headerlink" title="编辑器"></a>编辑器</h2>
                  <p>那么有编辑器支持这个吗？有，Microsoft Word，用它我们不用加空格，会自动给我们加好间距。 有人说，我平时不想用 Word，我就想用 Markdown，有编辑器吗？有，叫做 MarkEditor，它的 2.0 Pro 版本可以在打字的时候自动给我们添加空格。注意，这里是自动添加空格，不是自动留间距，是用空格的方式实现了间距。但是这个只能在你一个个打字的时候自动添加空格，如果把一个不带空格的话粘贴进去是不行的。另外 MarkEditor 解锁这个功能需要付费，所以我个人感觉其实不太划算的。 所以，平时还是自己手动加空格吧，经济实惠方便。 其他的编辑器如有好用的欢迎大家推荐哈。</p>
                  <h2 id="类库"><a href="#类库" class="headerlink" title="类库"></a>类库</h2>
                  <p>好吧，看到现在，你是不是现在都想把自己的中英文笔记加上空格了？难道要手调吗？不需要。 有现成的工具了，名字叫做 pangu，它支持各种语言，另外还有浏览器插件可以用，列表如下：</p>
                  <h3 id="浏览器插件"><a href="#浏览器插件" class="headerlink" title="浏览器插件"></a>浏览器插件</h3>
                  <ul>
                    <li><a href="https://chrome.google.com/webstore/detail/paphcfdffjnbcgkokihcdjliihicmbpd" target="_blank" rel="noopener">Google Chrome</a></li>
                    <li><a href="https://github.com/vinta/pangu.js/blob/master/browser_extensions/firefox/paranoid-auto-spacing.user.js" target="_blank" rel="noopener">Mozilla Firefox</a></li>
                  </ul>
                  <h3 id="开发工具包"><a href="#开发工具包" class="headerlink" title="开发工具包"></a>开发工具包</h3>
                  <ul>
                    <li>Official supports:<ul>
                        <li><a href="https://github.com/vinta/pangu" target="_blank" rel="noopener">pangu.go</a> (Go)</li>
                        <li><a href="https://github.com/vinta/pangu.java" target="_blank" rel="noopener">pangu.java</a> (Java)</li>
                        <li><a href="https://github.com/vinta/pangu.js" target="_blank" rel="noopener">pangu.js</a> (JavaScript)</li>
                        <li><a href="https://github.com/vinta/pangu.py" target="_blank" rel="noopener">pangu.py</a> (Python)</li>
                        <li><a href="https://github.com/vinta/pangu.space" target="_blank" rel="noopener">pangu.space</a> (Web API)</li>
                      </ul>
                    </li>
                    <li>Community supports:<ul>
                        <li><a href="https://github.com/coldnew/pangu.clj" target="_blank" rel="noopener">pangu.clj</a> (Clojure)</li>
                        <li><a href="https://github.com/SemonCat/pangu.dart" target="_blank" rel="noopener">pangu.dart</a> (Dart)</li>
                        <li><a href="https://github.com/cataska/pangu.ex" target="_blank" rel="noopener">pangu.ex</a> (Elixir)</li>
                        <li><a href="https://github.com/Cee/pangu.objective-c" target="_blank" rel="noopener">pangu.objective-c</a> (Objective-C)</li>
                        <li><a href="https://github.com/Kunr/pangu.php" target="_blank" rel="noopener">pangu.php</a> (PHP)</li>
                        <li><a href="https://github.com/dlackty/pangu.rb" target="_blank" rel="noopener">pangu.rb</a> (Ruby)</li>
                        <li><a href="https://github.com/airt/pangu.rs" target="_blank" rel="noopener">pangu.rs</a> (Rust)</li>
                        <li><a href="https://github.com/X140Yu/pangu.Swift" target="_blank" rel="noopener">pangu.swift</a> (Swift)</li>
                      </ul>
                    </li>
                  </ul>
                  <p>比如 Python 的话，就可以使用 pangu.py 这个包，GitHub 地址为：<a href="https://github.com/vinta/pangu.py，安装方式如下" target="_blank" rel="noopener">https://github.com/vinta/pangu.py，安装方式如下</a>：</p>
                  <figure class="highlight cmake">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip3 <span class="keyword">install</span> -U pangu</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这么用就好了：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import pangu</span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(pangu.spacing_text(<span class="string">'當你凝視著bug，bug也凝視著你'</span>)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果如下：</p>
                  <figure class="highlight armasm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">當你凝視著 <span class="keyword">bug，bug </span>也凝視著你</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>嗯，它自动给我们添加好了空格，非常不错。 不过这有点费劲，有简单一点的工具吗？ 有，我为此专门做了一个网页，功能很简单。 在左侧输入源文本，右侧就会显示添加空格之后的文本，页面如下： <img src="https://qiniu.cuiqingcai.com/2019-05-07-165203.png" alt="image-20190507222427295"> 这个是我用 Vue.js 开发的，实际上就是用了 pangu.js 这个库实现的，原理非常简单，主要目的就是为了方便空格排版。 另外这个网站我也部署了一下，叫做：<a href="http://space.cuiqingcai.com/" target="_blank" rel="noopener">http://space.cuiqingcai.com/</a>，大家以后也可以直接访问使用，以后我有想调整的文本，直接就用它了。 如果大家想获取源码，可以在公众号「进击的Coder」回复”空格”。 希望对大家有所帮助。 最后，为了世界的美好与和平，加个空格吧！</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-05-08 00:53:14" itemprop="dateCreated datePublished" datetime="2019-05-08T00:53:14+08:00">2019-05-08</time>
                </span>
                <span id="/6533.html" class="post-meta-item leancloud_visitors" data-flag-title="中英文排版空格问题解决方案" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>3.5k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>3 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6519.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6519.html" class="post-title-link" itemprop="url">Python 中拼音库 PyPinyin 的用法</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>最近碰到了一个问题，项目中很多文件都是接手过来的中文命名的一些素材，结果在部署的时候文件名全都乱码了，导致项目无法正常运行。 后来请教了一位大佬怎么解决文件名乱码的问题，他说这个需要正面解决吗？不需要，把文件名全部改掉，文件名永远不要用中文，永远不要。 我想他这么说的话，一定也是凭经验得出来的。 这里也友情提示大家，项目里面文件永远不要用中文，永远不要！ 好，那不用中文用啥？平时来看，一般我们都会用英文来命名，一般也不会出现中文，比如 resource, controller, result, view, spider 等等，所以绝大多数情况下，是不会出现什么问题的。但是也有个别的情况，比如一些素材、资源文件可能的中文命名的，那么这时候该咋办呢？ 首先像，因为是中文资源文件，我们要改成非中文命名的，无非两种，一种是英文，一种是拼音。 如果改英文，当然可以翻译、我们想翻译的话，逐个人工翻译成本太高，机器翻译的话，翻译完可能有些文不对题了，而且我们自己也不知道一些奇怪的资源英语应该叫什么，所以到时候真的找起来都找不到了。 所以第二种解决方案，那就是拼音了。中文转拼音，很自然，而且一个字就对应一串拼音，而且也非常容易从拼音看懂是什么意思，所以这确实是一个不错的方案。 那么问题就来了，怎样把一批中文文件转拼音命名呢？下面就让我们来了解 Python 的一个库 PyPinyin 吧！</p>
                  <h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2>
                  <p>Python 中提供了汉字转拼音的库，名字叫做 PyPinyin，可以用于汉字注音、排序、检索等等场合，是基于 hotto/pinyin 这个库开发的，一些站点链接如下：</p>
                  <ul>
                    <li>GitHub: <a href="https://github.com/mozillazg/python-pinyin" target="_blank" rel="noopener">https://github.com/mozillazg/python-pinyin</a></li>
                    <li>文档：<a href="https://pypinyin.readthedocs.io/zh_CN/master/" target="_blank" rel="noopener">https://pypinyin.readthedocs.io/zh_CN/master/</a></li>
                    <li>PyPi：<a href="https://pypi.org/project/pypinyin/" target="_blank" rel="noopener">https://pypi.org/project/pypinyin/</a></li>
                  </ul>
                  <p>它有这么几个特性：</p>
                  <ul>
                    <li>根据词组智能匹配最正确的拼音。</li>
                    <li>支持多音字。</li>
                    <li>简单的繁体支持, 注音支持。</li>
                    <li>支持多种不同拼音/注音风格。</li>
                  </ul>
                  <p>是不是等不及了呢？那就让我们来了解一下它的用法吧！</p>
                  <h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2>
                  <p>首先就是这个库的安装了，通过 pip 安装即可：</p>
                  <figure class="highlight cmake">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">pip3 <span class="keyword">install</span> pypinyin</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>安装完成之后导入一下这个库，如果不报错，那就说明安装成功了。</p>
                  <figure class="highlight elm">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">\&gt;&gt;&gt; <span class="keyword">import</span> pypinyin</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>好，接下来我们看下它的具体功能。</p>
                  <h2 id="基本拼音"><a href="#基本拼音" class="headerlink" title="基本拼音"></a>基本拼音</h2>
                  <p>首先我们进行一下基本的拼音转换，方法非常简单，直接调用 pinyin 方法即可：</p>
                  <figure class="highlight coffeescript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> pypinyin <span class="keyword">import</span> pinyin</span><br><span class="line"><span class="built_in">print</span>(pinyin(<span class="string">'中心'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight json">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[['zhōng'], ['xīn']]</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到结果会是一个二维的列表，每个元素都另外成了一个列表，其中包含了每个字的读音。 那么如果这个词是多音字咋办呢？比如 “朝阳”，它有两个读音，我们拿来试下：</p>
                  <figure class="highlight coffeescript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> pypinyin <span class="keyword">import</span> pinyin</span><br><span class="line"><span class="built_in">print</span>(pinyin(<span class="string">'朝阳'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight json">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[['zhāo'], ['yáng']]</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>好吧，它只给出来了一个读音，但是如果我们想要另外一种读音咋办呢？ 其实很简单，只需添加 heteronym 参数并设置为 True 就好了，我们试下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> pypinyin import pinyin</span><br><span class="line"><span class="builtin-name">print</span>(pinyin(<span class="string">'朝阳'</span>, <span class="attribute">heteronym</span>=<span class="literal">True</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[[<span class="symbol">'zhāo</span>', <span class="symbol">'cháo</span>'], [<span class="symbol">'yáng</span>']]</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>OK 了，这下子就显示出来了两个读音了，而且我们也明白了结果为什么是一个二维列表，因为里面的一维的结果可能是多个，比如多音字的情况就是这样。 但这个多少解析起来有点麻烦，很多情况下我们是不需要管多音字的，我们只是用它来转换一下名字而已，而处理上面的二维数组又比较麻烦。 所以有没有一个方法直接给我们一个一维列表呢？有！ 我们可以使用 lazy_pinyin 这个方法来生成，尝试一下：</p>
                  <figure class="highlight coffeescript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> pypinyin <span class="keyword">import</span> lazy_pinyin</span><br><span class="line"><span class="built_in">print</span>(lazy_pinyin(<span class="string">'聪明的小兔子'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[<span class="symbol">'cong</span>', <span class="symbol">'ming</span>', <span class="symbol">'de</span>', <span class="symbol">'xiao</span>', <span class="symbol">'tu</span>', <span class="symbol">'zi</span>']</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这时候观察到得到的是一个列表，并且不再包含音调了。 这里我们就有一个疑问了，为啥 pinyin 方法返回的结果默认是带音调的，而 lazy_pinyin 是不带的，这里面就涉及到一个风格转换的问题了。</p>
                  <h2 id="风格转换"><a href="#风格转换" class="headerlink" title="风格转换"></a>风格转换</h2>
                  <p>我们可以对结果进行一些风格转换，比如不带声调风格、标准声调风格、声调在拼音之后、声调在韵母之后、注音风格等等，比如我们想要声调放在拼音后面，可以这么来实现：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> pypinyin import lazy_pinyin, Style</span><br><span class="line"></span><br><span class="line">style = Style.TONE3</span><br><span class="line"><span class="builtin-name">print</span>(lazy_pinyin(<span class="string">'聪明的小兔子'</span>, <span class="attribute">style</span>=style))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[<span class="symbol">'cong1</span>', <span class="symbol">'ming2</span>', <span class="symbol">'de</span>', <span class="symbol">'xiao3</span>', <span class="symbol">'tu4</span>', <span class="symbol">'zi</span>']</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到运行结果每个拼音后面就多了一个声调，这就是其中的一个风格，叫做 TONE3，其实还有很多风格，下面是我从源码里面找出来的定义：</p>
                  <figure class="highlight ini">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="comment">#: 普通风格，不带声调。如： 中国 -&gt; ``zhong guo``</span></span><br><span class="line"><span class="attr">NORMAL</span> = <span class="number">0</span></span><br><span class="line"><span class="comment">#: 标准声调风格，拼音声调在韵母第一个字母上（默认风格）。如： 中国 -&gt; ``zhōng guó``</span></span><br><span class="line"><span class="attr">TONE</span> = <span class="number">1</span></span><br><span class="line"><span class="comment">#: 声调风格2，即拼音声调在各个韵母之后，用数字 [1-4] 进行表示。如： 中国 -&gt; ``zho1ng guo2``</span></span><br><span class="line"><span class="attr">TONE2</span> = <span class="number">2</span></span><br><span class="line"><span class="comment">#: 声调风格3，即拼音声调在各个拼音之后，用数字 [1-4] 进行表示。如： 中国 -&gt; ``zhong1 guo2``</span></span><br><span class="line"><span class="attr">TONE3</span> = <span class="number">8</span></span><br><span class="line"><span class="comment">#: 声母风格，只返回各个拼音的声母部分（注：有的拼音没有声母，详见 `#27`_）。如： 中国 -&gt; ``zh g``</span></span><br><span class="line"><span class="attr">INITIALS</span> = <span class="number">3</span></span><br><span class="line"><span class="comment">#: 首字母风格，只返回拼音的首字母部分。如： 中国 -&gt; ``z g``</span></span><br><span class="line"><span class="attr">FIRST_LETTER</span> = <span class="number">4</span></span><br><span class="line"><span class="comment">#: 韵母风格，只返回各个拼音的韵母部分，不带声调。如： 中国 -&gt; ``ong uo``</span></span><br><span class="line"><span class="attr">FINALS</span> = <span class="number">5</span></span><br><span class="line"><span class="comment">#: 标准韵母风格，带声调，声调在韵母第一个字母上。如：中国 -&gt; ``ōng uó``</span></span><br><span class="line"><span class="attr">FINALS_TONE</span> = <span class="number">6</span></span><br><span class="line"><span class="comment">#: 韵母风格2，带声调，声调在各个韵母之后，用数字 [1-4] 进行表示。如： 中国 -&gt; ``o1ng uo2``</span></span><br><span class="line"><span class="attr">FINALS_TONE2</span> = <span class="number">7</span></span><br><span class="line"><span class="comment">#: 韵母风格3，带声调，声调在各个拼音之后，用数字 [1-4] 进行表示。如： 中国 -&gt; ``ong1 uo2``</span></span><br><span class="line"><span class="attr">FINALS_TONE3</span> = <span class="number">9</span></span><br><span class="line"><span class="comment">#: 注音风格，带声调，阴平（第一声）不标。如： 中国 -&gt; ``ㄓㄨㄥ ㄍㄨㄛˊ``</span></span><br><span class="line"><span class="attr">BOPOMOFO</span> = <span class="number">10</span></span><br><span class="line"><span class="comment">#: 注音风格，仅首字母。如： 中国 -&gt; ``ㄓ ㄍ``</span></span><br><span class="line"><span class="attr">BOPOMOFO_FIRST</span> = <span class="number">11</span></span><br><span class="line"><span class="comment">#: 汉语拼音与俄语字母对照风格，声调在各个拼音之后，用数字 [1-4] 进行表示。如： 中国 -&gt; ``чжун1 го2``</span></span><br><span class="line"><span class="attr">CYRILLIC</span> = <span class="number">12</span></span><br><span class="line"><span class="comment">#: 汉语拼音与俄语字母对照风格，仅首字母。如： 中国 -&gt; ``ч г``</span></span><br><span class="line"><span class="attr">CYRILLIC_FIRST</span> = <span class="number">13</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>有了这些，我们就可以轻松地实现风格转换了。 好，再回到原来的问题，为什么 pinyin 的方法默认带声调，而 lazy_pinyin 方法不带声调，答案就是：它们二者使用的默认风格不同，我们看下它的函数定义就知道了： pinyin 方法的定义如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">def pinyin(hans, <span class="attribute">style</span>=Style.TONE, <span class="attribute">heteronym</span>=<span class="literal">False</span>, <span class="attribute">errors</span>=<span class="string">'default'</span>, <span class="attribute">strict</span>=<span class="literal">True</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>lazy_pinyin 方法的定义如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">def lazy_pinyin(hans, <span class="attribute">style</span>=Style.NORMAL, <span class="attribute">errors</span>=<span class="string">'default'</span>, <span class="attribute">strict</span>=<span class="literal">True</span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这下懂了吧，因为 pinyin 方法默认使用了 TONE 的风格，而 lazy_pinyin 方法默认使用了 NORMAL 的风格，所以就导致二者返回风格不同了。 好了，有了这两个函数的定义，我们再来研究下其他的参数，比如定义里面的 errors 和 strict 参数又怎么用呢？</p>
                  <h2 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h2>
                  <p>在这里我们先做一个测试，比如我们传入无法转拼音的字，比如：</p>
                  <figure class="highlight coffeescript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> pypinyin <span class="keyword">import</span> lazy_pinyin</span><br><span class="line"><span class="built_in">print</span>(lazy_pinyin(<span class="string">'你好☆☆，我是xxx'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>其中包含了星号两个，还有标点一个，另外还包含了一个 xxx 英文字符，结果会是什么呢？</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[<span class="symbol">'ni</span>', <span class="symbol">'hao</span>', <span class="symbol">'☆☆，</span>', <span class="symbol">'wo</span>', <span class="symbol">'shi</span>', <span class="symbol">'xxx</span>']</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可以看到结果中星号和英文字符都作为一个整体并原模原样返回了。 那么这种特殊字符可以单独进行处理吗？当然可以，这里就用到刚才提到的 errors 参数了。 errors 参数是有几种模式的：</p>
                  <ul>
                    <li>default：默认行为，不处理，原木原样返回</li>
                    <li>ignore：忽略字符，直接抛掉</li>
                    <li>replace：直接替换为去掉 <code>u</code> 的 unicode 编码</li>
                    <li>callable 对象：当传入一个可调用的对象的时候，则可以自定义处理方式。</li>
                  </ul>
                  <p>下面是 errors 这个参数的源码实现逻辑：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_handle_nopinyin_char</span><span class="params">(chars, errors=<span class="string">'default'</span>)</span>:</span></span><br><span class="line">    <span class="string">"""处理没有拼音的字符"""</span></span><br><span class="line">    <span class="keyword">if</span> callable_check(errors):</span><br><span class="line">        <span class="keyword">return</span> errors(chars)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> errors == <span class="string">'default'</span>:</span><br><span class="line">        <span class="keyword">return</span> chars</span><br><span class="line">    <span class="keyword">elif</span> errors == <span class="string">'ignore'</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">    <span class="keyword">elif</span> errors == <span class="string">'replace'</span>:</span><br><span class="line">        <span class="keyword">if</span> len(chars) &gt; <span class="number">1</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">''</span>.join(text_type(<span class="string">'%x'</span> % ord(x)) <span class="keyword">for</span> x <span class="keyword">in</span> chars)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> text_type(<span class="string">'%x'</span> % ord(chars))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>当处理没有拼音的字符的时候，errors 的不同参数会有不同的处理结果，更详细的逻辑可以翻看源码。 好了，下面我们来尝试一下，比如我们想将不能转拼音的字符去掉，则可以这么设置：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> pypinyin import lazy_pinyin</span><br><span class="line"><span class="builtin-name">print</span>(lazy_pinyin(<span class="string">'你好☆☆，我是xxx'</span>, <span class="attribute">errors</span>=<span class="string">'ignore'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[<span class="symbol">'ni</span>', <span class="symbol">'hao</span>', <span class="symbol">'wo</span>', <span class="symbol">'shi</span>']</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果我们想要自定义处理，比如把 <code>☆</code> 转化为 <code>※</code>，则可以这么设置：</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">print(<span class="keyword">lazy</span><span class="constructor">_pinyin('你好☆☆，我是<span class="params">xxx</span>', <span class="params">errors</span>=<span class="params">lambda</span> <span class="params">item</span>: ''.<span class="params">join</span>(['※' <span class="params">if</span> <span class="params">c</span> <span class="operator">==</span> '☆' <span class="params">else</span> <span class="params">c</span> <span class="params">for</span> <span class="params">c</span> <span class="params">in</span> <span class="params">item</span>])</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[<span class="symbol">'ni</span>', <span class="symbol">'hao</span>', <span class="symbol">'※※，</span>', <span class="symbol">'wo</span>', <span class="symbol">'shi</span>', <span class="symbol">'xxx</span>']</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如上便是一些相关异常处理的操作，我们可以随心所欲地处理自己想处理的字符了。</p>
                  <h2 id="严格模式"><a href="#严格模式" class="headerlink" title="严格模式"></a>严格模式</h2>
                  <p>最后再看下 strict 模式，这个参数用于控制处理声母和韵母时是否严格遵循 <a href="http://www.moe.edu.cn/s78/A19/yxs_left/moe_810/s230/195802/t19580201_186000.html" target="_blank" rel="noopener">《汉语拼音方案》</a> 标准。 下面的一些说明来源于官方文档： 当 strict 参数为 True 时根据 <a href="http://www.moe.edu.cn/s78/A19/yxs_left/moe_810/s230/195802/t19580201_186000.html" target="_blank" rel="noopener">《汉语拼音方案》</a> 的如下规则处理声母、在韵母相关风格下还原正确的韵母：</p>
                  <ul>
                    <li>21 个声母： <code>b p m f d t n l g k h j q x zh ch sh r z c s</code> （<strong>y, w 不是声母</strong>）</li>
                    <li>i行的韵母，前面没有声母的时候，写成yi(衣)，ya(呀)，ye(耶)，yao(腰)，you(忧)，yan(烟)， yin(因)，yang(央)，ying(英)，yong(雍)。（<strong>y 不是声母</strong>）</li>
                    <li>u行的韵母，前面没有声母的时候，写成wu(乌)，wa(蛙)，wo(窝)，wai(歪)，wei(威)，wan(弯)， wen(温)，wang(汪)，weng(翁)。（<strong>w 不是声母</strong>）</li>
                    <li>ü行的韵母，前面没有声母的时候，写成yu(迂)，yue(约)，yuan(冤)，yun(晕)；ü上两点省略。 （<strong>韵母相关风格下还原正确的韵母 ü</strong>）</li>
                    <li>ü行的韵跟声母j，q，x拼的时候，写成ju(居)，qu(区)，xu(虚)，ü上两点也省略； 但是跟声母n，l拼的时候，仍然写成nü(女)，lü(吕)。（<strong>韵母相关风格下还原正确的韵母 ü</strong>）</li>
                    <li>iou，uei，uen前面加声母的时候，写成iu，ui，un。例如niu(牛)，gui(归)，lun(论)。 （<strong>韵母相关风格下还原正确的韵母 iou，uei，uen</strong>）</li>
                  </ul>
                  <p>当 strict 为 False 时就是不遵守上面的规则来处理声母和韵母， 比如：<code>y</code>, <code>w</code> 会被当做声母，yu(迂) 的韵母就是一般认为的 <code>u</code> 等。 具体差异可以查看源码中 <a href="https://github.com/mozillazg/python-pinyin/blob/master/tests/test_standard.py" target="_blank" rel="noopener">tests/test_standard.py</a> 中的对比结果测试用例。</p>
                  <h2 id="自定义拼音"><a href="#自定义拼音" class="headerlink" title="自定义拼音"></a>自定义拼音</h2>
                  <p>如果对库返回的结果不满意，我们还可以自定义自己的拼音库，这里用到的方法就有 load_single_dict 和 load_phrases_dict 方法了。 比如刚才我们看到 “朝阳” 两个字的发音默认返回的是 zhao yang，我们想默认返回 chao yang，那可以这么做：</p>
                  <figure class="highlight stylus">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from pypinyin import lazy_pinyin, load_phrases_dict</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(lazy_pinyin(<span class="string">'朝阳'</span>)</span></span>)</span><br><span class="line">personalized_dict = &#123;</span><br><span class="line">    <span class="string">'朝阳'</span>: [[<span class="string">'cháo'</span>], [<span class="string">'yáng'</span>]]</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="title">load_phrases_dict</span><span class="params">(personalized_dict)</span></span></span><br><span class="line"><span class="function"><span class="title">print</span><span class="params">(lazy_pinyin(<span class="string">'朝阳'</span>)</span></span>)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们自定义了一个词典，然后使用 load_phrases_dict 方法设置了一下就可以了。 运行结果：</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[<span class="symbol">'zhao</span>', <span class="symbol">'yang</span>']</span><br><span class="line">[<span class="symbol">'chao</span>', <span class="symbol">'yang</span>']</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样就可以完成自定义的设置了。 在一些项目里面我们可以自定义很多拼音库，然后加载就可以了。 另外我们还可以注册样式实现自定义，比如将某个拼音前面加上 Emoji 表情，样例：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span> pypinyin.style <span class="keyword">import</span> register</span><br><span class="line"><span class="keyword">from</span> pypinyin <span class="keyword">import</span> lazy_pinyin</span><br><span class="line"></span><br><span class="line"><span class="meta">@register('kiss')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">kiss</span><span class="params">(pinyin, **kwargs)</span>:</span></span><br><span class="line">    <span class="keyword">if</span> pinyin == <span class="string">'me'</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f'?<span class="subst">&#123;pinyin&#125;</span>'</span></span><br><span class="line">    <span class="keyword">return</span> pinyin</span><br><span class="line"></span><br><span class="line">print(lazy_pinyin(<span class="string">'么么哒'</span>, style=<span class="string">'kiss'</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>运行结果：</p>
                  <figure class="highlight scheme">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">[<span class="symbol">'?me</span>', <span class="symbol">'?me</span>', <span class="symbol">'dá</span>']</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里我们调用 register 方法注册了一个样式 style，然后转换的时候指定即可，通过观察运行结果我们可以发现，这样我们就可以将 me 字的拼音前面加上 ? 这个 Emoji 表情了。 以上就是 PyPinyin 这个库的基本用法，更多的用法建议大家看看源码或者看 API 文档：<a href="https://pypinyin.readthedocs.io/zh_CN/master/api.html" target="_blank" rel="noopener">https://pypinyin.readthedocs.io/zh_CN/master/api.html</a>。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-04-04 17:06:50" itemprop="dateCreated datePublished" datetime="2019-04-04T17:06:50+08:00">2019-04-04</time>
                </span>
                <span id="/6519.html" class="post-meta-item leancloud_visitors" data-flag-title="Python 中拼音库 PyPinyin 的用法" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>6k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>5 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6511.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> 个人随笔 <i class="label-arrow"></i>
                  </a>
                  <a href="/6511.html" class="post-title-link" itemprop="url">从学校迈入职场</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>正式入职微软，提交了第一个 PR 之后，我坐在椅子上思考人生。终于我也变成了一名正式的企业员工，变成了一名正式的踏入社会的职业人士，从此我的学生生涯也算是画上了一个句号，不，更确切的说应该是画上了一个引号。 和同事租了房子，生活条件算是还不错，有了属于自己的房间，有了专属自己的衣柜、书橱、办公桌，想要的硬件、软件、日常用品想配就配，算是应有尽有了。首先日常生活上最大的感受就是自己的生活条件变得更好、更自由，不再像学校一样有各种限制了，虽然日常花销变多了，但总体上来说我更喜欢现在的生活环境。</p>
                  <h2 id="回顾"><a href="#回顾" class="headerlink" title="回顾"></a>回顾</h2>
                  <p>先回想一下自己的学生生活吧，初高中就不说了，就天天上课为了高考，后来在大学基本上就是三点一线，宿舍——食堂——教室/实验室，然后读研，研究生模式也差不多，宿舍——食堂——实验室/公司。</p>
                  <h3 id="编程入门"><a href="#编程入门" class="headerlink" title="编程入门"></a>编程入门</h3>
                  <p>我大学的时候选的就是计算机科学与技术这个专业，当时学校开的第一门入门课就是 Java，当时可以说是对编程一窍不通，什么 print？打印机吗？控制台是什么？控制谁？什么面向对象？我对象在哪里？都是些什么玩意。就这样，随着老师的课堂洗脑和一些并不怎么感兴趣的编程作业，我慢慢理解了原来 print 是这么个玩意，对象原来不是那个对象，就慢慢对编程建立了一个概念。插句话，其实我感觉编程的一些概念和思维还是很重要的，有人说编程学不会，可能就是脑中没有形成一个比较清晰的概念，想清楚它能做到什么，怎样从用双手解决一件事的思维转换成用编程解决一件事的思维。 扯远了，但那时候仅仅 Java 是一门课而已，虽然最后考试考得还不错，但是还是不是很懂它能为我带来些什么东西。后来大学就开了一个叫做课程设计的课，意思就是说让自己动手编程实现一个可以操作的项目。我们当时学校要求的就是实现一个黑白棋在线对战系统，当时可把我为难坏了。后来了解到了这里面还挺复杂的，又得编写界面又得搞一些算法，还得搞一个在线 WebSocket 通信，当时可以说是毫无思路，然后就去网上搜一些 Java 的教程，当时是搜到了马士兵的 Java 课就顺着看了起来，可以说马士兵是我的 Java 最重要的启蒙老师了，慢慢地把一些原理和基础学会了之后就有了基本的编程思路了，开始上手编，做界面，做服务器等等。另外还有一些小插曲，有的界面还得抠图，当时为了打造一个完美的棋盘效果还学了 PS 来花了一个木质棋盘和黑白棋子，估计得花了好几个月的时间终于做出来了一个像样点的系统，虽然现在源码已经找不到了，但真的说这个课程设计真的让我理解了编程的一些思维和理念以及它能为我带来些什么，我能够用它做到什么，脑中的一些概念变得更加清晰了，收获非常大。 后来学校开了数据结构和算法的一些课，慢慢地我又对一些基础的算法和 C++ 的一些编程语言有了一定的了解。再后来就是一些基础专业课了，比如操作系统，计算机组成原理，计算机网络等等，总体来说其实我没太感觉出具体有多大作用，但你要假设我没学过，我可能有很多东西都不知其理。人就是这样，有些东西在拥有的时候觉不出有什么好的，但一旦没有才会有明显的感觉。</p>
                  <h3 id="加实验室"><a href="#加实验室" class="headerlink" title="加实验室"></a>加实验室</h3>
                  <p>好吧又扯远了，然后就可以说迈入了我人生中一个比较重要的点了，那就是加入学校的一个实验室。之前许多东西我和室友自己瞎倒腾，比如当时进行版本控制的话，就是自己手动压缩一下，命名项目的时候添加一个版本号并用下划线分割，后来进了实验室才知道还有 Git 这么牛逼的东西，于是乎就跟着学习了 Git，了解了 GitHub，觉得整个代码世界都光明了。当时我加入的是后台组，一开始是从 PHP 开始学起的，从原生的 PHP，到普通的 CodeIgniter 框架，到高级的 Laravel 框架，当时写的时候主要用是 MVC 模式，所以前端的东西也难免需要用到的，所以那会儿又学习了前端的一些知识，慢慢地就变成了 Web 前后端通吃，自己也可以逐渐完成一些大型项目的开发工作。当时还自己开通了博客来记录自己学习的一些经验，然后跟着实验室一起接外包做外包，做了不下十个门户及商业网站的开发。</p>
                  <h3 id="学习爬虫"><a href="#学习爬虫" class="headerlink" title="学习爬虫"></a>学习爬虫</h3>
                  <p>再往后可能就是临近大学毕业的时候了，那会儿实验室的一位学长写了一些爬虫的入门文章，当时也跟着学了起来，边学边记录，学的整理的一些知识点都放在了自己的博客上。后来又探索了一些新的爬取方案，也一并整理到博客上了，形成了一个入门到进阶的一套简易版教程，后来随着写的越来越多，来看的人也越来越多了，后来访问量也逐渐上来了，现在的话日均访问量可以达到 15000+。 再往后可能就差不多大学毕业了，当时由于是保研到北航的，所以就提前来到了北京开始了研究生的预备工作，也节省了不少时间。那会儿就有充足的时间来做自己的事情，比如学习一些网络课程，继续做一些关于爬虫的研究工作。当时随着我的博客访问量越来越大，图灵便联系我看能不能写一本关于爬虫的书，当时想的一个是可以把自己学习的知识好好整理一下，还可以作为自己的一部个人作品出版出来，的确是一件非常不错的事情，所以当时就答应下来了。不得不说写书的过程是非常艰辛的，舍去了好多平时的休息时间，同时还发现了自己的很多不足的地方去查漏补缺，最终也不得不延期了好几个月才交上稿。后来又审校了非常久的时间，到去年五月份才出版出来，定名字叫《Python3网络爬虫开发实战》。不过后来的销量还算不错，现在已经重印了 10 次，50000 本了。现在还在继续撰写第二版中，把一些过期的案例和知识点更新，再把一些新的技术加进去。</p>
                  <h3 id="研究生生活"><a href="#研究生生活" class="headerlink" title="研究生生活"></a>研究生生活</h3>
                  <p>OK，当然研究生阶段也不是都写书了。研究生阶段其实一开始是比较迷茫的，其实当时并不知道自己毕业之后要做什么方面的工作以及想去哪里。最初读的时候是选了网络安全的方向，做一些 Web 渗透方面的研究，后来觉得研究得差不多了，就又转了自然语言处理的方向，从吴恩达的机器学习开始学起，然后了解了深度学习的一些模型，又了解了自然语言处理的一些知识。与此同时，我也在微软这边当实习生，从最初的爬虫、 Platform 再到 Science NLP 研究，慢慢地也认识了一些大佬，和他们一起交流的确让我学习到了不少。 由于我在微软这边实习时间不短，所以当时也参加了实习生转正的面试。微软整个的面试流程还是很规范和严格的，包括多面技术面，另外每一面的要求也都不低的。首先最基础的要求就是算法，给你几道题目，来白纸上把这道题的代码写出来，面试官会非常注重边界处理和细节把握，如果要写不出来，基本上也离凉凉不远了。接下来还有一些基本的公式推导，比如如果要面试机器学习算法工程师的话，可能会让手写推导 SVM、LR 等算法。另外还有一些系统设计题，来看看你的思维和架构是不是能达到要求。最后我记得还有一些智力题，看看反应得快不快。再往后的面试也是谈谈自己对行业的一些理解和看法，谈得还是比较深入的。总之考察得非常综合，当时准备面试的期间真的是无比焦虑，感觉人心惶惶的，当时在疯狂地刷题，复习各种算法推导，准备了也差不多有一两个月。最后得知 Offer 的那一刻，一块石头终于落地了。最后我也如愿入职了微软小冰，今年三月刚刚入职，也希望能为小冰带来更多的贡献，也希望大家可以多多关注微软小冰。 其实我已经在微软这边实习了一年多的时间了，平时很多时间也都呆在公司里，自己也算提前一步迈入职场了，经过我个人实习的体验和感受，同时也结合自己平时的了解，总结出来了一些经验。当然这仅仅是我个人的一些看法，仁者见仁智者见智，在这里仅仅做一些经验总结和分享，总结一些从学生迈入职场之后，我会注重的一些地方。如果对你有些启发，那是再好不过了。</p>
                  <h2 id="工作相关"><a href="#工作相关" class="headerlink" title="工作相关"></a>工作相关</h2>
                  <p>首先就是工作相关的一些东西了，由学生到工作，我个人觉得还是有一些需要调整的地方的，下面稍微这里说一下。</p>
                  <h3 id="转换思维"><a href="#转换思维" class="headerlink" title="转换思维"></a>转换思维</h3>
                  <p>学生到职场的转换，第一个重要的就是转变自己看待事情的思维。迈入职场，就别再有一种”我是学生“、”我刚刚毕业“ 这样的想法，在职场里别人才不管我们是不是刚毕业的学生，他们看的基本上都是我们能不能完成工作或者配合他们完成工作。 另外也别总有一种”努力必有回报“这样的思维。学生时代，可能一道题解不出来，一个项目做不出来，努努力很大程度上还是可以能解的。但是到了职场，这个很不一样，很多事情并不是一定存在因果这样的线性关系的。比如说某个项目你辛辛苦苦做了很久，可能就因为领导不想要这个功能而直接砍掉了。比如说你辛辛苦苦写了稿子，可能因为和某个评阅人的想法不一致而被直接拒掉了。想开点，有时候就是这么操蛋。 还有一个就是别把一件事想的太简单，在工作中，其实很多事上牵扯的东西是很多的。学生的一件事可能就是一件事，一道题可能就是一套题。工作中一件事可能不仅仅是一件事，它所关系到的东西要复杂的多。我们可能会考虑到对公司、对领导、对同事、对绩效、对家人的很多事情，多考虑考虑。 总之，思维的转换是第一步，别再像之前当学生的时候一样了。</p>
                  <h3 id="靠真本事"><a href="#靠真本事" class="headerlink" title="靠真本事"></a>靠真本事</h3>
                  <p>首先得考虑清楚啊，公司把我们招来是为了让我们来发挥价值的。 而我们的价值在哪里发挥？当然是体现在工作成果中。那成果哪里来的？那当然是把自己的能力转化过来的。 不同职位有不同的要求，首先确保的是要利用自己的能力把本职工作做好。当然我是做技术的，我所专注的就是技术这个领域了，技术能力是必不可少的，当然做这份工作需要的其实也不仅仅是技术能力，还有一些非表面意义上的能力，如学习探索能力、沟通合作能力等等。 而这些吧，归根结底还是要靠自己的真本事的。别想着投机取巧，别想着耍点小聪明，虽然一时方便了，但是长远来看，吃亏的还是自己。 当然这例子很多，比如明明不是自己做的，却要为了某些目的非要伪装自己，被看穿之后，不少人其实是看破不说破的，慢慢地自己就会知道后果了。比如为了某些目标，背地里各种跟领导各种小恩小惠等，其实领导基本不会 Care 你这种小聪明，甚至还觉得这个人有点靠不住，自己的受信任度也会大打折扣，另外自己不会感到心虚吗？ 最好的办法是什么，其实就是把该做完的工作保质保量完成，靠自己的真本事，实打实做好要求的每一件事就好了，领导看重的就是这个。</p>
                  <h3 id="利益为先"><a href="#利益为先" class="headerlink" title="利益为先"></a>利益为先</h3>
                  <p>职场上面啊，你说人情，当然也是会有的，但是更多的人关注的其实是工作本身以及自己的利益。这和学校的差距还是很大的。比如你要是个学生的话，有些人可能觉得你的身份而稍微谅解一下，而踏入社会之后，基本就不会了。 比如你要跟某个人交流和合作，就别总是寒暄以此来套近乎，不是说这个一点用都没有，多少可能还是有点用的，如果你有价值，靠这个可能能快速拉近他与你的距离。但很多情况下，这个的效果可能真的作用不大。 到了职场，大家都很忙的，比如找人谈事情，稍微介绍几句，开口直接谈正事、谈利益就好了。如果不能为对方提供有价值的信息，或者提供的价值并不是对方想要的，对方会觉得比较浪费时间。这种时候就别掺杂什么别的什么交情之类的东西了，虽说我认为成年人只看利益不够准确，但基本上是没错的。 比如跟人谈个合作的时候，讲清楚两点，第一是我们能提供什么价值和服务，第二是他能得到什么利益。当然第二点有的情况下是不需要说的，因为我们本身提供的价值就是给对方的利益，对方觉得值，自然会跟我们合作的。其中也不乏一些沟通的小技巧，还需要在这个过程中慢慢摸索。</p>
                  <h3 id="保持交流"><a href="#保持交流" class="headerlink" title="保持交流"></a>保持交流</h3>
                  <p>领导给了一个要求，如果有疑问，要多问多交流，别偏离了方向，别是自作主张。 之前的时候，我接到了一个任务，了解了基本的需求时候就开始开工干。在做的过程中其实有不少不确定的地方，但是当时我也不知道是不好意思还是什么原因，就没有跟领导交流，按照自己的想法做了出来。最后演示的时候，领导说你怎么做成这样了？我本意不是这样的，你有疑问怎么不跟我讨论呢？ 我就意识到了，这其实也可以多少印证”选择比努力更重要“，方向性或方案性的东西是很重要的。 所以，遇到什么不确定的点，要多多沟通交流。当然这并不是说突然想到了某个点就去找对方讨论，把自己的疑问以及可能解决方案和结果梳理清楚再好好交流一下，效果一定好上太多。</p>
                  <h3 id="工作日志"><a href="#工作日志" class="headerlink" title="工作日志"></a>工作日志</h3>
                  <p>做好工作日志，时间长了，会发现这个非常有用，它的有用不仅仅是体现在它仅仅是做了总结，它也会潜移默化地影响我们自己。 比如每天可以找个时间简单记录一下自己今天做了什么事情，收获了什么，还需要做什么。这个记录的过程就是一个思考的过程，它会让我们反思自己的一些不足和需要做的更好的地方，会变成一种激励的。 另外工作日志，等某天你打开的时候，会发现成就感满满，同时写什么总计和汇报也就不用愁了。</p>
                  <h2 id="个人相关"><a href="#个人相关" class="headerlink" title="个人相关"></a>个人相关</h2>
                  <p>刚才所说的挺多都是和工作相关的，另外还有一些和个人相关的我觉得也应该好好注意下。</p>
                  <h3 id="迈出第一步"><a href="#迈出第一步" class="headerlink" title="迈出第一步"></a>迈出第一步</h3>
                  <p>我曾经尝试过很多坚持每天做点什么的事情，发现有的挺难坚持下来的，一而再再而三地累积，就变得越来越多，后来整个坚持的事情就失败了，这就是拖延症的一种现象。 拖延症应该很多人都会有，当然我也是。我平时分析拖延症的一个很大的原因就是迈不出第一步，进而一件事就搁置了。就比如说要去健身房，我觉得最难的就是出家门；比如说要每天刷一道算法题，最难的可能就是打开LeetCode界面。不知道你们什么意见，我至少是这么认为的。 因此，迈出第一步非常重要，迈出了第一步，可以说就成功了一半。</p>
                  <h3 id="言多必失"><a href="#言多必失" class="headerlink" title="言多必失"></a>言多必失</h3>
                  <p>的确是，言多必失，这话没毛病。 可能我们仅仅是刚入职的新人，很多情况下不该说的就别说。你永远也不知道你说了某些话之后，你在别人耳中会变成什么样的版本。另外如果我们突然说错了什么话，被人揪住了把柄，那可是很难的。所以，一些场景中，我们一些没必要说的就不要去说，不知道该说不该说的也不要说。 另外我个人比较反感的是明一套暗一套。我们不可能让所有人都认同我们自己，不认同我们的，他们表现出来，我们道不同不相为谋。认同我们的支持我们的，我们可以与他们成为朋友。但表面上显得非常友好，然而在背地里面却说坏话，这是非常令人反感的。</p>
                  <h3 id="终身学习"><a href="#终身学习" class="headerlink" title="终身学习"></a>终身学习</h3>
                  <p>虽然表面上看学生生涯结束了，但实际上迈入职场恰恰是一个新的开始，其实人与人之间的差距就是因为工作后的这些年逐渐拉开的，所以不论什么时候都不能放弃学习。 当然迈入职场以后，学习的一些侧重点可能就不太一样了。工作后学习的第一肯定是能用在岗位之上的专业知识，我们所掌握的知识一定至少要能够让我们顺利地专业地完成自己的任务。其次可以扩宽一下知识面，比如可以关注下经济、理财、交际等知识。 总之还是一句话，学习到的本事是别人所偷不走的，做一个终身学习者。</p>
                  <h3 id="做好记录"><a href="#做好记录" class="headerlink" title="做好记录"></a>做好记录</h3>
                  <p>很多事情是确实很有必要记录下来的。首先不瞒说，随着事情的增多，工作的忙碌，我发现比原来更加”忘事“了。后来我看了一篇报道，说其实并不是脑子记忆力下降了多少，而是集中的注意力变得更少了。 一件事如果注意力分配得少了，自然很多事情就不容易被我们记住。想想确实是这样的，现在我每天都把自己的计划安排得满满当当，现在连拿出时间好好读一本书的时间都不多了，因此就别提那些日常小事了。那咋办？随手记录下来，比如记到手机的备忘录或者自己的 TodoList 软件里面，然后再进一步安排如何执行就好。 当然记录并不仅仅局限于这个记录下来平时的闪念，平时的一些工作总结、学习笔记也可以时刻记录下来。我有记录学习笔记的习惯，当然肯定也有的时候有一些内容没有及时记录下来。过了一段时间，我发现唯一记得的就是自己曾经整理过笔记甚至发表博客的那些内容，没写过的或者没发过的基本都忘干净了。 记录，成为更好的自己。</p>
                  <h3 id="作息调整"><a href="#作息调整" class="headerlink" title="作息调整"></a>作息调整</h3>
                  <p>良好的作息还是非常有必要的。现在肯定非常多的朋友会倾向于熬夜，十二点之后才睡。我之前实习的时候，比这个更狠，经常一两点钟睡觉，睡到将近中午，就直接吃中午饭得了，然后一上午就没有了，当时就有一种半天已经被我浪费的感觉，会有一种负罪感。 后来我开始逐步调整我的作息，尽量早睡早起，从之前的九点多慢慢地调整到八点，甚至是七点多，醒来之后整理一些东西，开始全新的一天，整体的体验确实是比之前睡到中午好太多了。另外我个人也参加了一个早起打卡活动，现在已经坚持每天打卡将近一个月了，基本上也养成了早起的习惯。 另外工作之后，下班的时间也可以好好利用起来，比如晚上稍微拉伸一下，睡前记得喝水等等，由于我平时也每天对着电脑，所以也需要定期地起来活动一下。我把它们设置到滴答清单里面，每天都有软件的定时提醒，到了时间就做，一天天坚持下来。总体来说，感觉也比之前好多了。 所以从现在起，拿起笔好好规划下自己的目标和平时的作息吧。 另外可能大家听说过，某个老板每天只睡五个多小时，晚上工作到很晚才走，早上一大早就在了。我当时也比较好奇这到底是怎么做到的？难道有什么特殊的睡眠技巧？经过我的一阵搜罗，结果是，没有。BOSS 们并没有我们这么忙，可能早上开完会，我们去干活了，而人家去补觉了。所以，保证充足的睡眠还是很有必要的。</p>
                  <h3 id="合理分配时间"><a href="#合理分配时间" class="headerlink" title="合理分配时间"></a>合理分配时间</h3>
                  <p>从学生到工作，其实就相当于我们的事情又多了一部分，工作日大部分为工作时间，非工作日则大部分是自己的时间。另外工作之后，相比学生时代来说，事情可能也会变得更多更繁琐。那么如何合理分配自己的时间呢？ 我这边采用了目前比较流行的四象限时间管理法，他就是把处理的事按“重要”和“紧急”两个维度划分，并对应到四种待处理状态中，分别叫做”重要不紧急“，”重要紧急“，”不重要紧急“，”不重要不紧急“四个类别。 通常来说，我们需要马上执行“急事”，确保它们不会延期。但长远来看，我们最好将重心放在“要事”上。可能有一些紧急的小事，它确实是不重要的，但我们能比较快的做完，然后把它勾掉，所以现在很多人可能更加优先处理的是紧急的事情而不是重要事情，最后紧急的事情做完了，剩下重要的事情没做完，所以要么加班熬夜，要么就延期。回想起来，其实这是很得不偿失的，忙活了一天，重要的事情还是没完成。 所以，我们需要尽量减少”不重要紧急“事件的忙碌，尽早处理”重要不紧急“的事情，合理分配时间，去做对我们而言重要的事情，才是四象限理论的核心。 我自己使用了滴答清单这款软件，之前使用的是 Todoist，这两款软件都非常不错，但是由于滴答清单多了番茄时钟的功能，我就改用了滴答清单，我利用它建立了四个智能清单，分别叫做”重要不紧急“，”重要紧急“，”不重要紧急“，”不重要不紧急“，然后建立筛选条件，软件就会根据任务的优先级和到期日期自动更新。然后就可以每天查看、处理并调整任务——做完的事情及时打勾，有变更的事情及时移动，可以遵循这样的一些准则：</p>
                  <ul>
                    <li>优先执行重要且紧急的事件</li>
                    <li>尽量提前规划重要不紧急事件，在它们变得紧急前就完成</li>
                    <li>如果可以的话，试着将不重要但紧急的事情交由他人处理，或者学会对别人的请求说 Sorry</li>
                    <li>需要控制去做不重要不紧急事件的时间，不要过度放松</li>
                  </ul>
                  <p>有了这几条原则，我们就可以很好的分配我们的时间了，如果大家觉得有用的话也可以试一下。 以上就是我的成长历程以及我所想到的一些需要调整和坚持的一些做法，如果能为大家带来一些帮助，那就再好不过了。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-03-25 02:35:33" itemprop="dateCreated datePublished" datetime="2019-03-25T02:35:33+08:00">2019-03-25</time>
                </span>
                <span id="/6511.html" class="post-meta-item leancloud_visitors" data-flag-title="从学校迈入职场" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>7.6k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>7 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6509.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> 技术杂谈 <i class="label-arrow"></i>
                  </a>
                  <a href="/6509.html" class="post-title-link" itemprop="url">如何使用 SSH 控制连接 Windows 服务器</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>入职微软之后，这边大多数是使用 Windows 进行开发的，比如我的台式机是 Windows 的，还有一部分服务器是 Windows 的，当然 Linux 是也非常多。 很多情况下我是使用自己的 Mac 笔记本来远程连接我的 Windows 机器来开发的。比如如果我在工位上，我会用我的 Mac 连接两块显示屏，然后一种一块用来远程桌面连接我的 Windows 开发机，这样另外一块屏幕和 Mac 自带的屏幕就用来看文档或者使用 Teams 通讯等等。如果我回家了，我家里也是有两块屏，开上 VPN，照样用一块屏使用远程桌面，另外一块屏幕和 Mac 自带屏幕就可以做其他事情了。 这样就解决了一个问题：我的 Windows 基本上都是仅用作开发的，一块屏幕就开着一个 Visual Studio，其他的操作都会在 Mac 进行，比如查文档，发消息等等。这样我下班之后照样使用远程连接的方式来操作，和在公司就是一样的。这样就避免了一些软件的来回登录，比如如果我上班只用公司机器，下班了之后换了 Mac 还得切 Teams、切微信、切浏览器等等，还是很麻烦的，而且上班期间 Mac 就闲置了也不好。所以我就采取了这样的开发方案。</p>
                  <h2 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h2>
                  <p>有了这个情景，就引入了一个问题。开了一个远程桌面之后，我几乎一个屏幕都是被 Visual Studio 占据的，而远程桌面貌似只能开一个屏幕？如果我要再开一个终端窗口的话，那可能屏幕就不太够用了，或者它就得覆盖我的全屏 Visual Stuido。 另外我平时 Mac 终端软件都是使用 SSH 的，基本都是用来连 Linux 的，Windows 一般都是开远程桌面。但命令行这个情形的确让我头疼，让我感到不够爽，因为毕竟远程桌面之后，Windows 里面的操作都得挤在一个桌面里面操作了。当然可能能设置多个桌面，如果可以的话，麻烦大家告知一下谢谢。 所以解决的痛点在于：我要把一些操作尽量从 Windows 里面分离出来，例如终端软件，我能否在远程桌面外面操作，能否使用 SSH 来控制我的 Windows 机器。 好，有需求才有动力，说干就干。</p>
                  <h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2>
                  <p>查了一下，Windows 上其实也是有 SSH 服务器的，只不过默认是没有装的，这里只需要安装一个 OpenSSH 服务器就好了。 Win10 的话，就在设置里面可以安装，从开始菜单打开“设置”，然后选择应用和功能，这里就有一个“管理可选功能”的选项。 <img src="https://ws1.sinaimg.cn/large/006tKfTcgy1g17uv5tf19j30xb0puwm9.jpg" alt="image-20190319093941643"> 点击之后便可以看到一个可选功能，选择 OpenSSH 服务器即可，一般情况下是没有安装的。如果没有安装的话它会提示一个安装按钮，这里我已经安装好了，就提示了一个卸载按钮。 <img src="https://ws3.sinaimg.cn/large/006tKfTcgy1g17uwoakpij30x70pv0u6.jpg" alt="image-20190319094113033"> OK，有了它，直接点击安装即可完成 OpenSSH 服务器的安装。 当然如果你是想批量部署 Windows 服务器的话，当然是推荐使用 PowerShell 来自动化部署了。 首先需要用管理员身份启动 PowerShell，使用如下命令看一下，要确保 OpenSSH 可用于安装：</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">Get</span>-WindowsCapability -Online | ? <span class="type">Name</span> -<span class="keyword">like</span> <span class="string">'OpenSSH*'</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>输出应该是类似的结果：</p>
                  <figure class="highlight css">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="selector-tag">Name</span>  : <span class="selector-tag">OpenSSH</span><span class="selector-class">.Client</span>~~~~0<span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-class">.0</span></span><br><span class="line"><span class="selector-tag">State</span> : <span class="selector-tag">NotPresent</span></span><br><span class="line"><span class="selector-tag">Name</span>  : <span class="selector-tag">OpenSSH</span><span class="selector-class">.Server</span>~~~~0<span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-class">.0</span></span><br><span class="line"><span class="selector-tag">State</span> : <span class="selector-tag">NotPresent</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>然后使用 PowerShell 安装服务器即可：</p>
                  <figure class="highlight css">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="selector-tag">Add-WindowsCapability</span> <span class="selector-tag">-Online</span> <span class="selector-tag">-Name</span> <span class="selector-tag">OpenSSH</span><span class="selector-class">.Server</span>~~~~0<span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-class">.0</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>输出结果类似：</p>
                  <figure class="highlight yaml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="attr">Path          :</span></span><br><span class="line"><span class="attr">Online        :</span> <span class="literal">True</span></span><br><span class="line"><span class="attr">RestartNeeded :</span> <span class="literal">False</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这样也可以完成 OpenSSH 的安装。 安装完成之后，就需要进行一些初始化配置了，还是以管理员身份，使用 PowerShell 执行即可。 首先需要开启 SSHD 服务：</p>
                  <figure class="highlight crmsh">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="literal">Start</span>-Service sshd</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>然后设置服务的自动启动：</p>
                  <figure class="highlight delphi">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">Set</span>-Service -<span class="keyword">Name</span> sshd -StartupType <span class="string">'Automatic'</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>最后确认一下防火墙是否是放开的：</p>
                  <figure class="highlight pgsql">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">Get</span>-NetFirewallRule -<span class="type">Name</span> *ssh*</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>如果是放开的，那么结果会提示 <code>OpenSSH-Server-In-TCP</code>这个状态是 enabled。 好了，完成如上操作之后我们就可以使用 SSH 来连接我们的 Windows 服务器了。</p>
                  <h2 id="连接"><a href="#连接" class="headerlink" title="连接"></a>连接</h2>
                  <p>连接非常简单了，用户名密码就是 Windows 的用户名和密码，使用 IP 地址链接即可。 比如我的 Windows 开发机的局域网 IP 为：10.172.134.88，那么就可以使用如下命令完成链接：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">ssh <span class="symbol">user@</span><span class="number">10.172</span><span class="number">.134</span><span class="number">.88</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>然后输入密码，就连接成功了，和 Linux 的是一样的。 另外我自己现在 Mac 常用的 SSH 客户端工具有 Termius，可以多终端同步使用，非常方便，这里我只需要添加我的 Windows 机器就好了，如图所示： <img src="https://ws1.sinaimg.cn/large/006tKfTcgy1g17vz64duxj30zk0m8gn6.jpg" alt="image-20190319101812208"> OK，以后就可以非常轻松地用 SSH 连接我的 Windows 服务器了，爽歪歪，上面的需求也成功解决。 以上便是使用 SSH 来连接 Windows 服务器的方法，如果大家有需求可以试试。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-03-25 02:33:31" itemprop="dateCreated datePublished" datetime="2019-03-25T02:33:31+08:00">2019-03-25</time>
                </span>
                <span id="/6509.html" class="post-meta-item leancloud_visitors" data-flag-title="如何使用 SSH 控制连接 Windows 服务器" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>2k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>2 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6462.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6462.html" class="post-title-link" itemprop="url">程序猿的情人节怎么过？</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>都说程序猿是一类不解风情的生物，“赚的多，花的少，死的早”已经成为了程序猿的标志，“眼镜、格子衫、垢面蓬头、拖鞋裤衩”已然也成了程序猿的代表形象，“代码、游戏、老湿”也已经快要成了程序猿的生命。 但！有的时候，比如情人节，我们就可以发挥我们的特长了，我们程序猿也可以有自己的浪漫！ 不过这个第一步是，你得有一个女朋友（哦哦，是不是可以不用往下看了？ 那么有了第一步之后，下面我们应该怎么办呢？ 下面介绍一个比较实用的可以送给女朋友的礼物（这其实也是我今天送给女朋友的礼物嘿嘿。 首先想想，作为程序猿，我们的专长是什么？废话，当然是代码。 有了代码，还需要送口红吗？还需要送包包吗？还需要送鲜花吗？废话，都有了代码了，这些当然就….还是要送的。万一写的代码你女朋友看不懂那岂不是死翘翘了。 好那送完了口红或包包或鲜花之后，确保已经平安无事了，我们就可以再发挥我们的光和热了（听起来咋这么奇怪呢？ 进入正题，那我们可以利用代码做点什么呢？想想可以做文章的地方有什么，你们的纪念日，你们曾经做过的事情，你们在一起的时间，这些都是属于你们的独一无二的，我们可以想方设法把它们和代码联系起来。 那怎么发给女朋友看呢？做个 App、小程序、网页什么的都是可以的吧，其中网页可能是做起来最快最方便的了，然后配上一个专属域名，简直美滋滋。 好，那一想，基本方向就确定了，直接开干，接下来就描述一下我准备这个礼物的历程吧。 对于我来说，我就计划做一个网页，同时用代码的形式把和女朋友在一起的时间呈现出来，通过网页的动效来呈现我们在一起的时间，另外还计划把我们之间的故事用代码关联表示出来。 本来我打算是从零开始手撸一个的，但是一些组件比如动画特效，还有一些倒计时的组件是相对比较难做的，于是我就在 GitHub 上逛了一下，看了几个示例，找到了一个和我理想作品差不多的项目，然后在它的基础上做了一些改动，就成了最终的效果。 主要功能如下：</p>
                  <ul>
                    <li>第一是通过代码来表述出来和女朋友之间的故事。由于我和女朋友是因为 Python 认识的，而且我们两个平时都会写一些 Python，所以我决定用 Python 来写出我们之间的故事，加上 Python 的注释来辅助描述每一行代码的意义。</li>
                    <li>第二是通过代码来呈现我和女朋友在一起的时间。这里就用上了一些动画特效和秒数计时方案，实时地呈现出来我和女朋友在一起已经有多久了。</li>
                  </ul>
                  <p>最终完成之后的效果是这样子的： <img src="https://ws2.sinaimg.cn/large/006tKfTcly1g062do1uo8j31al0u0axr.jpg" alt="预览图"> 然后由于我自己有一个域名，叫做 cuiqingcai.com，然后我就把它设置了二级域名解析，二级域名名称就叫做 love，域名最终就是 love.cuiqingcai.com。 最终的效果大家可以扫码或者复制链接查看一下最终的效果：<a href="http://love.cuiqingcai.com/，二维码如下：" target="_blank" rel="noopener">http://love.cuiqingcai.com/，二维码如下：</a> <img src="https://ws1.sinaimg.cn/large/006tKfTcly1g064wqfgqvj30ea0eadg5.jpg" alt="二维码"> 感觉还可以吧？如果你也想送这样的礼物的话，可以根据我现有的代码来进行修改，我已经将源码放到 GitHub 了，地址为：<a href="https://github.com/Germey/ValentinesDay，大家可以修改源码，把它变成属于你和你女朋友的专属页面，然后送给女朋友。" target="_blank" rel="noopener">https://github.com/Germey/ValentinesDay，大家可以修改源码，把它变成属于你和你女朋友的专属页面，然后送给女朋友。</a> 下面说一些关键的技术和需要修改的点。</p>
                  <h2 id="代码动画"><a href="#代码动画" class="headerlink" title="代码动画"></a>代码动画</h2>
                  <p>打开页面之后，我们可以看到页面的代码是一个字一个字敲出来的，这实际上是利用了一个定时器来实现的。 首先我们可以预定义好所有的文本，然后动画播放的时候，首先把所有的文本隐藏，然后每隔几十毫秒读取一个字符，然后将其呈现出来。由于文本本身就是换行的，所以在呈现的时候就会一行一行地像打字机一样呈现出来。 另外为了模拟打字的效果，在呈现的时候可以在最后的字符后面添加一个下划线符号，模拟打字的效果。 其关键的实现代码如下：</p>
                  <figure class="highlight clojure">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">(<span class="name">function</span> (<span class="name">a</span>) &#123;</span><br><span class="line">    a.fn.typewriter = function () &#123;</span><br><span class="line">        this.each(<span class="name">function</span> () &#123;</span><br><span class="line">            var d = a(<span class="name">this</span>), c = d.html(), b = <span class="number">0</span><span class="comment">;</span></span><br><span class="line">            d.html(<span class="string">""</span>)<span class="comment">;</span></span><br><span class="line">            var e = setInterval(<span class="name">function</span> () &#123;</span><br><span class="line">                var f = c.substr(<span class="name">b</span>, <span class="number">1</span>)<span class="comment">;</span></span><br><span class="line">                if (<span class="name">f</span> == <span class="string">"&lt;"</span>) &#123;</span><br><span class="line">                    b = c.indexOf(<span class="string">"&gt;"</span>, b) + <span class="number">1</span></span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    b++</span><br><span class="line">                &#125;</span><br><span class="line">                d.html(<span class="name">c.substring</span>(<span class="number">0</span>, b) + (<span class="name">b</span> &amp; <span class="number">1</span> ? <span class="string">"_"</span> : <span class="string">""</span>))<span class="comment">;</span></span><br><span class="line">                if (<span class="name">b</span> &gt;= c.length) &#123;</span><br><span class="line">                    clearInterval(<span class="name">e</span>)</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;, <span class="number">75</span>)</span><br><span class="line">        &#125;)<span class="comment">;</span></span><br><span class="line">        return this</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)(<span class="name">jQuery</span>)<span class="comment">;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里可以看到，首先获取了页面代码区域的内容，然后通过 DOM 操作将代码先清空，然后利用 setInterval 方法设置一个定时器，定时间隔 75 毫秒，也就是说 75 毫秒循环调用一次。每调用一次，就会从原来的字符上多取一个字符，然后尾部拼接一个下划线就好了。</p>
                  <h2 id="代码内容"><a href="#代码内容" class="headerlink" title="代码内容"></a>代码内容</h2>
                  <p>接下来就是代码内容了，这里面要想好怎样把一些关键时间来表示出来。比如和女朋友怎样认识的，后来什么时间在一起的，一起做过什么事情，将来有什么计划和打算，都可以来描述出来，另外编程语言可以选择你喜欢的语言，然后配以一定的注释来描述代码所代表的含义。 我和女朋友是在 PyCon 认识的，也算是因为 Python 结缘，然后平时我们都会写一些 Python，所以我就选用 Python 作为编程语言了。 然后我又加上了我们认识的时间、在一起的时间、一起做过的事情，然后再配以一段代码来表达自己的想法，其中的一些灵感也是我看了一些案例想出来的，在表述过程中我使用了面向对象的思维声明了两个对象，一个代表我，一个代表我女朋友，然后一起做过的事情就可以通过对象调用方法的形式来表述出来了，另外一些动作和标志可以通过自定义方法或者代码的参数来表示出来，其中每一行代码的动作我都配以一条 Python 的注释来完成，注释当然是用英文，一些话我还用了翻译软件一句句查的。 然后最后我用了一段 Python 代码来表达了自己的感情，内容如下：</p>
                  <figure class="highlight properties">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="comment"># You are the greatest love of my life.</span></span><br><span class="line"><span class="attr">while</span> <span class="string">True:</span></span><br><span class="line">    <span class="attr">if</span> <span class="string">u.with(i):</span></span><br><span class="line">        <span class="attr">you</span> = <span class="string">everything</span></span><br><span class="line">    <span class="attr">else</span>:<span class="string"></span></span><br><span class="line">        <span class="attr">everything</span> = <span class="string">u</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这个代码的含义叫做“无论天涯海角，你都是我的一切。“，一个 while True 循环代表了永久。 这些代码其实都是在 HTML 代码中预定义好的，其中注释需要用 span 标签配以 comments 的 class 来修饰，缩进需要用 span 标签配以 placeholder 的 class 来修饰，例如：</p>
                  <figure class="highlight xml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"comments"</span>&gt;</span># You are the greatest love of my life.<span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">while <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"keyword"</span>&gt;</span>True<span class="tag">&lt;/<span class="name">span</span>&gt;</span>:<span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"placeholder"</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"keyword"</span>&gt;</span>if<span class="tag">&lt;/<span class="name">span</span>&gt;</span> u.with(i):<span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"placeholder"</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"placeholder"</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span>you = everything<span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"placeholder"</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"keyword"</span>&gt;</span>else<span class="tag">&lt;/<span class="name">span</span>&gt;</span>:<span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"placeholder"</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"placeholder"</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span>everything = u<span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里不同的格式用 span 的不同 class 来标识，空格缩进一个 placeholder 是两个空格，comments 代表注释格式，关键词使用 keyword 来标识。如果你需要自定义自己的内容，通过控制这些内容穿插写入就好了。</p>
                  <h2 id="纪念日计时"><a href="#纪念日计时" class="headerlink" title="纪念日计时"></a>纪念日计时</h2>
                  <p>关于纪念日，这个实现起来其实挺简单的，就是首先定义好你们的纪念日，然后获取当前系统时间，然后计算秒数差值，然后将其转化为天数、小时数即可，关键核心代码实现如下：</p>
                  <figure class="highlight javascript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">timeElapse</span>(<span class="params">c</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> e = <span class="built_in">Date</span>();</span><br><span class="line">    <span class="keyword">var</span> f = (<span class="built_in">Date</span>.parse(e) - <span class="built_in">Date</span>.parse(c)) / <span class="number">1000</span>;</span><br><span class="line">    <span class="keyword">var</span> g = <span class="built_in">Math</span>.floor(f / (<span class="number">3600</span> * <span class="number">24</span>));</span><br><span class="line">    f = f % (<span class="number">3600</span> * <span class="number">24</span>);</span><br><span class="line">    <span class="keyword">var</span> b = <span class="built_in">Math</span>.floor(f / <span class="number">3600</span>);</span><br><span class="line">    <span class="keyword">if</span> (b &lt; <span class="number">10</span>) &#123;</span><br><span class="line">        b = <span class="string">"0"</span> + b</span><br><span class="line">    &#125;</span><br><span class="line">    f = f % <span class="number">3600</span>;</span><br><span class="line">    <span class="keyword">var</span> d = <span class="built_in">Math</span>.floor(f / <span class="number">60</span>);</span><br><span class="line">    <span class="keyword">if</span> (d &lt; <span class="number">10</span>) &#123;</span><br><span class="line">        d = <span class="string">"0"</span> + d</span><br><span class="line">    &#125;</span><br><span class="line">    f = f % <span class="number">60</span>;</span><br><span class="line">    <span class="keyword">if</span> (f &lt; <span class="number">10</span>) &#123;</span><br><span class="line">        f = <span class="string">"0"</span> + f</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>另外它也是通过一个定时器来实现的时间刷新，每隔 500 毫秒调用一次：</p>
                  <figure class="highlight lisp">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">setInterval(<span class="name">function</span> () &#123;</span><br><span class="line">    timeElapse(<span class="name">together</span>)<span class="comment">;</span></span><br><span class="line">&#125;, <span class="number">500</span>)<span class="comment">;</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="动画心形"><a href="#动画心形" class="headerlink" title="动画心形"></a>动画心形</h2>
                  <p>动画心形，其实这个实现起来是很巧妙的。这里在画的时候实际上是利用了贝塞尔曲线来绘制一个心形，同时在在画的过程中还加了花开的效果，花开的效果使用了随机数和随机颜色生成。 其中动画画心形的核心代码如下：</p>
                  <figure class="highlight kotlin">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">Petal.prototype = &#123;</span><br><span class="line">    draw: function () &#123;</span><br><span class="line">        <span class="keyword">var</span> a = <span class="keyword">this</span>.bloom.garden.ctx;</span><br><span class="line">        <span class="keyword">var</span> e, d, c, b;</span><br><span class="line">        e = new Vector(<span class="number">0</span>, <span class="keyword">this</span>.r).rotate(Garden.degrad(<span class="keyword">this</span>.startAngle));</span><br><span class="line">        d = e.clone().rotate(Garden.degrad(<span class="keyword">this</span>.angle));</span><br><span class="line">        c = e.clone().mult(<span class="keyword">this</span>.stretchA);</span><br><span class="line">        b = d.clone().mult(<span class="keyword">this</span>.stretchB);</span><br><span class="line">        a.strokeStyle = <span class="keyword">this</span>.bloom.c;</span><br><span class="line">        a.beginPath();</span><br><span class="line">        a.moveTo(e.x, e.y);</span><br><span class="line">        a.bezierCurveTo(c.x, c.y, b.x, b.y, d.x, d.y);</span><br><span class="line">        a.stroke()</span><br><span class="line">    &#125;, render: function () &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span>.r &lt;= <span class="keyword">this</span>.bloom.r) &#123;</span><br><span class="line">            <span class="keyword">this</span>.r += <span class="keyword">this</span>.growFactor;</span><br><span class="line">            <span class="keyword">this</span>.draw()</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">this</span>.isfinished = <span class="literal">true</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里最关键的一个部分就是 bezierCurveTo，这里传入的是绘制贝塞尔曲线的参数坐标，那这些坐标怎么生成的呢，这里是利用了数学上的一个桃心线方程，如图所示： <img src="https://ws3.sinaimg.cn/large/006tKfTcly1g06493zp47j30x30u0h2f.jpg" alt="贝塞尔曲线"> 其中心形线的解析方程为： <script type="math/tex">x = 16(sint)^3 \\ y = 13cost-5cos2t-2cos3t-cos4t</script> 这个公式代表了绘制坐标点的 x、y 的解析方程，用代码表示出来就是：</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">function</span> get<span class="constructor">HeartPoint(<span class="params">c</span>)</span> &#123;</span><br><span class="line">    var b = c<span class="operator"> / </span>Math.PI;</span><br><span class="line">    var a = <span class="number">19.5</span><span class="operator"> * </span>(<span class="number">16</span><span class="operator"> * </span><span class="module-access"><span class="module"><span class="identifier">Math</span>.</span></span>pow(<span class="module-access"><span class="module"><span class="identifier">Math</span>.</span></span>sin(b), <span class="number">3</span>));</span><br><span class="line">    var d = -<span class="number">20</span><span class="operator"> * </span>(<span class="number">13</span><span class="operator"> * </span><span class="module-access"><span class="module"><span class="identifier">Math</span>.</span></span>cos(b) - <span class="number">5</span><span class="operator"> * </span><span class="module-access"><span class="module"><span class="identifier">Math</span>.</span></span>cos(<span class="number">2</span><span class="operator"> * </span>b) - <span class="number">2</span><span class="operator"> * </span><span class="module-access"><span class="module"><span class="identifier">Math</span>.</span></span>cos(<span class="number">3</span><span class="operator"> * </span>b) - <span class="module-access"><span class="module"><span class="identifier">Math</span>.</span></span>cos(<span class="number">4</span><span class="operator"> * </span>b));</span><br><span class="line">    return <span class="keyword">new</span> <span class="constructor">Array(<span class="params">offsetX</span> + <span class="params">a</span>, <span class="params">offsetY</span> + <span class="params">d</span>)</span></span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里是生产了心形线方程的 x、y 坐标，然后再以此绘制出带有动画效果的心形。 最终呈现的效果就是现在你看到的样子。 不过这些在改代码的时候实际上不用关心，只需要修改你们在一起的时间就好了，就是代码中的这一行：</p>
                  <figure class="highlight angelscript">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">together.setFullYear(<span class="number">2018</span>, <span class="number">10</span>, <span class="number">5</span>);</span><br><span class="line">together.setHours(<span class="number">15</span>);</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里修改你们在一起的时间和小时就可以了，然后页面就会自动更新你们在一起多久了，并动态呈现出来了。</p>
                  <h2 id="域名解析"><a href="#域名解析" class="headerlink" title="域名解析"></a>域名解析</h2>
                  <p>对于域名解析，这个建议大家可以申请一个域名，比如我的域名就是 cuiqingcai.com，我可以设置一个二级域名解析，叫做 love.cuiqingcai.com。 如果没有域名的话可以现买一个，比如阿里云、腾讯云购买，然后设置解析即可。 如果没有域名，也可以使用一些虚拟云服务器，他们会帮你设置二级域名，当然也可以使用 GitHub Pages，甚至你使用 IP 地址来访问也是没问题的。</p>
                  <h2 id="项目代码"><a href="#项目代码" class="headerlink" title="项目代码"></a>项目代码</h2>
                  <p>项目的代码我都放在了：<a href="https://github.com/Germey/ValentinesDay，大家可以自行修改成想要的样子送给女朋友，只能帮你到这里啦。" target="_blank" rel="noopener">https://github.com/Germey/ValentinesDay，大家可以自行修改成想要的样子送给女朋友，只能帮你到这里啦。</a> 嘿嘿，这就是我今天送给女朋友的礼物，女朋友收到了开心得不得了，开心。</p>
                  <h2 id="我的礼物"><a href="#我的礼物" class="headerlink" title="我的礼物"></a>我的礼物</h2>
                  <p>其实我今天也收到了女朋友送的特殊的礼物，可以说她确实花了不少心思啊，她送了我什么呢？令我没想到的是，她居然刚申请了一个微店，然后她微店上架了好多商品，我看到时候惊呆了，店铺如图所示： <img src="https://ws2.sinaimg.cn/large/006tKfTcly1g064lc6433j30u01hck6t.jpg" alt="image-20190214182451019"> 里面上架了什么商品？洗水果服务？做饭刷碗服务？捏肩膀服务？还有自动哄老婆机？我惊了。 她把商品发给我，我好奇问她这是干嘛的。 她说：要获得我的洗水果服务，捏肩膀服务，只需要在我的小店里购买使用就好了（作掐腰状）！还有自动哄老婆机，你要惹我生气了，只需要购买一个自动哄老婆机，我就会不生气了！嘿嘿合不合算？ <img src="https://ws4.sinaimg.cn/large/006tKfTcly1g0652dxmglj30cg0ck0zf.jpg" alt=""> 我说：多少钱？999！这么贵的吗！ 她说：当然不是啦，亲亲我们店里有活动的，使用优惠券满 999 减 998 呢，您是我的 VIP 唯一专属客户，我会给您发优惠券的呀，使用优惠券只需要一块钱就可以购买了。购买之后，您每次使用一张，我就可以给您洗水果、捏肩膀了！这个情人节的话呢，我要送亲亲 10 张！可省着点话，不能累到店长我啊！ 哦哦，卧槽真牛逼啊！于是乎我就快快乐乐领取到了十张优惠券购买了女朋友的这些服务，等着时不时用一张，享受一下帝王级的待遇，美滋滋！哈哈~ 最后，祝大家情人节快乐！幸福！</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-02-21 09:50:13" itemprop="dateCreated datePublished" datetime="2019-02-21T09:50:13+08:00">2019-02-21</time>
                </span>
                <span id="/6462.html" class="post-meta-item leancloud_visitors" data-flag-title="程序猿的情人节怎么过？" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>5.9k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>5 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6457.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> 技术杂谈 <i class="label-arrow"></i>
                  </a>
                  <a href="/6457.html" class="post-title-link" itemprop="url">分享一套 MacBook 高效开发工具</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>作为一名程序员，能够利用好工具提高开发和工作效率是非常重要的。我个人使用的都是苹果系列产品，电脑为 MacBook Pro 15 寸，手机 iPhone 7P，另外还有一个 iPad Pro 和一副 Apple Watch。我一直觉得 Mac 是非常适合做程序开发的，它既有比较不错的页面，也有类 Unix 的操作系统，使得日常使用和程序开发都极其便利，另外由于苹果本身自有的 iCloud 机制，使用 Mac、iPhone、iPad 跨平台开发和工作也变得十分便利。</p>
                  <p>近期我又对自己的一些工具进行了整理，弃用了一些工具，新启用了一些工具。目的也只有一个，就是提高自己的工作和开发效率，让生活变得更美好。如果你也在用 Mac 开发，或者你也有使用 iPad、iPhone，下面我所总结的个人的一些工具或许能给你带来帮助。</p>
                  <h2 id="快速导航"><a href="#快速导航" class="headerlink" title="快速导航"></a>快速导航</h2>
                  <p>这是 Mac 上的一个工具，要说到提高效率，首推 Alfred，可以说是 Mac 必备软件，利用它我们可以快速地进行各种操作，大幅提高工作效率，如快速打开某个软件、快速打开某个链接、快速搜索某个文档，快速定位某个文件，快速查看本机 IP，快速定义某个色值，几乎我们能想到的都能对接实现。</p>
                  <p>其实 Mac 本身已经自带了软件搜索还有 Spotlight，但是其功能还是远远比不上 Alfred，有了它，所有的快捷操作几乎都能实现。</p>
                  <p>这些快速功能是怎么实现的呢？实际上是 Alfred 对接了很多 Workflow，我们可以使用 Workflow 方便地进行功能扩展，一些比较优秀的 Workflow 已经有人专门做过整理了，可以参见：<a href="https://github.com/zenorocha/alfred-workflows" target="_blank" rel="noopener">https://github.com/zenorocha/alfred-workflows</a>，大家可以安装自己所需要的 Workflow，大大提高效率。</p>
                  <h2 id="复制粘贴"><a href="#复制粘贴" class="headerlink" title="复制粘贴"></a>复制粘贴</h2>
                  <p>Mac 上默认只有一个粘贴板，当我们新复制了一段文字之后，如果我们想再找寻之前复制的历史记录就找不到了，这其实是很反人类的。</p>
                  <p>好在 Paste 这款软件帮我们解决了这个问题，它可以保存我们粘贴板的历史记录，等需要粘贴某个内容的时候只需要呼出 Paste 历史粘贴板，然后选择某个特定的内容粘贴就好了，另外它还支持文本格式调整粘贴板分类和搜索，还可以支持快速便捷粘贴。有了它，再也不用担心粘贴板丢失了！</p>
                  <p>另外使用 Mac 和 iPhone、iPad 之间也可以相互之间复制粘贴，可以在一台 Apple 设备上拷贝文本、图像、照片和视频，然后在另一台 Apple 设备上粘贴该内容。例如，可以拷贝在 Mac 上浏览网页时发现的食谱，然后将其粘贴到附近 iPhone 上“备忘录”中的购物清单。这是在 macOS Sierra 版本之后出来的功能，若要使用需要确保 Mac 的版本是 Sierra 及以后。若要使用，几个设备必须满足“连续互通”系统要求。它们还必须在“系统偏好设置”（在 Mac 上）和“设置”（在 iOS 设备上）中打开 Wi-Fi、蓝牙和 Handoff，另外必须在所有设备上使用同一 Apple ID登录 iCloud。</p>
                  <p>具体的操作流程可以参见苹果的官网说明：<a href="https://support.apple.com/kb/PH25168" target="_blank" rel="noopener">https://support.apple.com/kb/PH25168</a>，有了这个功能，日常的一些操作便可以直接同步了，甚至不再需要 AirDrop，更不需要微信和邮件。</p>
                  <h2 id="时间管理"><a href="#时间管理" class="headerlink" title="时间管理"></a>时间管理</h2>
                  <p>现在这个时候，时间比什么都重要，每个人的时间都是公平的，如果我们能够合理规划好自己的时间和工作，这就跨出了成功的一步。</p>
                  <p>我曾经尝试用手写的方式来记录自己的一些任务，但总感觉它有一些并不方便的地方。比如某时某刻突然想起来，想要添加一件事情或者完成了一件事情，或者想要修改截止时间，或者想要划分优先级，其实都不怎么方便。最好的方式还是通过一些专用的时间管理软件来分配分配和管理自己的时间。</p>
                  <p>我曾经使用过非常多款时间管理工具，最终我选择的是 Todoist，这个是我感觉体验非常不错的一款。这个软件里面基本的任务添加与勾划功能当然必不可少，它也支持优先级管理，分类管理，时间设置，另外还有几个我觉得非常加分的几个点，比如：</p>
                  <ul>
                    <li>
                      <p>添加时间时可以直接通过一句话来添加，比如”每隔两天晚上九点运动”，它会自动识别并设置为循环任务，并能在相应的时刻提醒你。</p>
                    </li>
                    <li>
                      <p>支持全部平台，不论是网页还是 Windows、Mac、Android、iPhone、iPad、Apple Watch、Chrome、Firefox，你能想到的平台，应有尽有。</p>
                    </li>
                    <li>
                      <p>它还支持事件同步，可以在 Mac 或者 iPhone 的日历中添加 Todoist 的同步，这样你所有的事情都会被定时同步到日历软件中，这样日历中就既包含了节日、生日等信息，又可以把每天我们需要做的事同步进来，日程信息一目了然。这样你就可以把日历变成一个提醒器，设置什么时候提醒就好了，现在我就在用 Mac 或者 iPhone 上的日历来提醒我什么时间该做什么事情了。</p>
                    </li>
                    <li>
                      <p>它还支持多人写作，就类似于 Worktile、Teambition、Trello，我曾经使用这款软件完成了多个项目的任务分配和多人协作开发，还是非常方便的。</p>
                    </li>
                    <li>
                      <p>另外它还支持过期智能重新安排任务，比如有一些任务没有完成，它还可以根据优先级来重新进行时间规划和安排，同时也有任务评分和目标评价机制，来反映我们任务完成情况。</p>
                    </li>
                  </ul>
                  <p>另外关于时间管理还有一个非常重要的四象限法则，大家也可以了解一下。有了这个法则，大家可以合理安排优先级，合理分配每个任务的时间。有关于我的时间管理经验我后面还会详细写一篇相关的文章，介绍一下我平时会怎样进行时间规划和学习的。</p>
                  <p>另外我还尝试过番茄土豆这个软件，这个软件的缺点在于整体的功能还比较简陋，而且不能和我已有的 Todo List 进行同步。好处就是可以自己设置番茄，保持专注工作。但目前我尚未发现满意的产品，如有还希望大家可以留言推荐一下，谢谢。</p>
                  <h2 id="笔记记录"><a href="#笔记记录" class="headerlink" title="笔记记录"></a>笔记记录</h2>
                  <p>在学习的时候来进行记录是非常非常重要的，强烈建议一边学习一边把所做所想记录下来，最后做一下整理成文。一方面方便查阅，另一方面加深印象和理解。</p>
                  <p>Markdown 想必大家都已经很耳熟了，现在我写文章或者笔记几乎全都用 Markdown 来写，现在很多云笔记也慢慢逐步支持 Markdown 的语法了，我的博客后台也自己配置了 Markdown 的支持。不过也有某些平台尚未支持 Markdown，比如知乎，忍不住吐槽一句，知乎的编辑器实在用得是心累，当然可以使用插件来解决，也当然也有所好，我就不再说什么了。不过我还是强烈推荐 Markdown 来进行写作和记录的，用过之后你可能就不再想用 Word 了。</p>
                  <p>言归正传，既然谈到笔记和写作。我的笔记本是 Mac，之前几乎所有的笔记，包括写书，都几乎是在 Mac 上完成的，但是确实有的时候是不方便的。比如 Mac 不在身边或者想用 iPhone 或者 iPad 来写点东西的时候，一个需要解决的问题就是云同步问题。有了云同步，我们如果在电脑上写了一部分内容，接着切换了另一台台式机，或者切换了手机的时候，照样能够接着在原来的基础上写，非常方便。</p>
                  <p>这时候可能就有小伙伴推荐有道云笔记、印象笔记等软件，它们支持 Markdown，但这并不是它们的主打支持方向，对 Makrdown 的支持当然没有一些专业的 Makrdown 编辑工具专业。对我个人而言，我不想因为它们自带了云同步而抛弃了纯粹的 Makrdown 写作环境，我只想要一个纯粹的 Makrdown 写作环境，而不想引入比如有道云里面的普通笔记、签到等冗余的功能，也不想看到里面的广告推荐等内容。所以对于云同步，我使用了另外的解决方案。对于写作软件，我也摸索出了自己的一套方案。</p>
                  <p>对于电脑端的 Markdown 写作软件，推荐两款，一款是 Typora，另一款是 MarkEditor。</p>
                  <p>对于 Makrdown 编辑器来说，我觉得有几个比较重要的点：</p>
                  <ul>
                    <li>
                      <p>不能纯写 Makrdown，要实时地能够看到自己写完 Makrdown 之后最终呈现的效果是怎样的。</p>
                    </li>
                    <li>
                      <p>插入图片要方便，很多编辑器需要先将图片挪到某个文件夹下或者上传图片才可以插入图片，这是很不友好的，如果能够直接通过复制粘贴的形式插入图片，甚至能够自动将图片上传到云端，那就再好不过了。</p>
                    </li>
                    <li>
                      <p>能够打开一整个 Makrdown 文件夹，左侧显示文件列表，右侧进行写作编辑，不能仅仅支持一个 Markdown 文件的编辑。</p>
                    </li>
                    <li>
                      <p>如果需要用到公司，那么编辑器需要对 Markdown 公式支持比较好。</p>
                    </li>
                  </ul>
                  <p>以上介绍的这两款软件都可以做到。</p>
                  <ul>
                    <li>
                      <p>Typora 是免费的，更加轻量级，而且支持即写即得，界面支持和公式支持都比较好，图片的话可以结合 iPic 软件直接上传到图床，同时也可以直接将复制的图片直接粘贴到编辑器中，非常友好，目前我正在使用。</p>
                    </li>
                    <li>
                      <p>MarkEditor 是收费的，功能更为丰富，支持左右分栏模式、阅读模式等，它也支持直接复制和粘贴图片，另外还有强大的导出功能，还可以直接将文件发布为一个网站等等，也十分推荐。</p>
                    </li>
                  </ul>
                  <p>不过目前由于 Typora 更轻量级，并且能和 iPic 而且功能配合使用，粘贴后的图片可以点击直接上传到云端，非常方便，我目前已经由 MarkEditor 切换到 Typora 了。</p>
                  <p>写作界面如下：</p>
                  <p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1g0508a70joj31jw0u0au6.jpg" alt=""></p>
                  <p>如图这是打开了一个文件夹，这个文件夹里面有好多 Makrdown 文件，都是我在研究和学习过程中所写的笔记。</p>
                  <p>然后需要解决的就是云同步问题，云同步其实使用网盘就足够了。由于我使用 Mac，所以我选用了 iCloud，开了 200G 的空间，足够了。这样我所记录的内容能够秒级同步到 iCloud Drive 中，这样我再使用 iPhone、iPad 就可以直接看到最新的内容了。当然还有一些推荐的，比如 OneDrive、谷歌云等多种云盘同步工具，哪个方便用哪个。Mac 和 iPhone 的好处就是已经内置了 iCloud Drive，所以不用再去在各个终端上配置了。</p>
                  <p>接下来就是在其他的电脑以及 iPhone、iPad 上进行写作的解决方案了。由于我的文件都已经存放在了 iCloud Drive，所以就需要一款 Makrdown 编辑软件可以直接读写 iCloud Drive 里面的内容，同时界面还要友好，功能完善一点。在这里我最终选择了 Markdown Pro，它的功能简洁但是又比较完善，打开之后直接选取 iCloud Drive 里面的 Makrdown 文件即可开始编辑，并且它是左右分栏的，即左侧编辑，右侧预览，非常方便简洁，另外它对公式的支持也很好，下图是我在 iPad 上对本文进行编辑的效果预览图。</p>
                  <p><img src="https://ws2.sinaimg.cn/large/006tNc79ly1g055n71vrvj31400u0npd.jpg" alt=""></p>
                  <p>对于图片的插入，在 iPhone 和 iPad 上我借助了另外一个工具，叫做 SM.MS，这个软件可以直接选取图片，然后上传到云端，点击复制即可得到链接和 Makrdown 图片链接，所以插图也不是什么问题了。</p>
                  <p><img src="https://ws2.sinaimg.cn/large/006tNc79gy1g0578rt4s6j30u01hcnpd.jpg" alt=""></p>
                  <p>如图所示，上传照片之后，便会出现各种各样的图片链接形式，有纯链接、HTML、Markdown 等等，直接点击复制按钮即可复制，然后粘贴到文档中。</p>
                  <p>另外如果你用了 Windows 的话，只要下载一个 iCloud 云盘软件即可同步。如果使用的是其他的云盘软件，也只需要配置一下就好了。</p>
                  <p>有了这套，我们就可以实现随时随地写笔记，Mac、iPhone、iPad 无缝切换。</p>
                  <h2 id="思维导图"><a href="#思维导图" class="headerlink" title="思维导图"></a>思维导图</h2>
                  <p>很多时候我们在构思方案或者流程的时候需要对思维做梳理，或者在列方案呈现的时候也需要分门别类地进行呈现。这时候大多数情况下就需要用到一个工具，思维导图。</p>
                  <p>思维导图工具我个人使用的是 MindNode，在 Mac 上用它可以通过各种快捷键快速的增删思维导图节点，另外界面也非常绚丽多彩。</p>
                  <p>对于思维导图软件来说，我也希望能全平台同步，其实 MindNode 也有对应的移动端软件，同样是 MindNode，二者可以通过 iCloud Drive 进行同步，同样可以做到无缝衔接。</p>
                  <p>另外还有很多朋友也在用 XMind，功能同样很强大，大家也可以试试。</p>
                  <h2 id="远程控制"><a href="#远程控制" class="headerlink" title="远程控制"></a>远程控制</h2>
                  <p>我们经常会和各种服务器打交道，例如我们经常使用 SSH 来远程连接某台 Linux 服务器，原生 Terminal 是支持 SSH 的，但你会发现原生带的这个太难用了。可能很多小伙伴使用 iTerm，不得不说这确实是个神器，大大方便了远程管理流程。但我在这里还要推荐一个我经常使用的 SSH Shell，没错，它的名字就是 SSH Shell，它的页面操作简洁，同时管理和记录远程主机十分方便，另外还支持秘钥管理、自动重连、自定义主题等等功能，个人用起来十分顺手，强烈推荐！</p>
                  <p>当然除了电脑，当我们出去在外的时候，紧急情况也可能需要使用 SSH 来连接和管理我们的服务器，所以我也在 iPhone 和 iPad 上装了远程管理软件，叫做 Termius，同样功能十分强大，快捷操作十分便捷，有免费的试用期限，我觉得非常好用就订购了，推荐给大家。</p>
                  <p><img src="https://ws3.sinaimg.cn/large/006tNc79gy1g057v8tli7j30u01hc46y.jpg" alt=""></p>
                  <h2 id="代码记录"><a href="#代码记录" class="headerlink" title="代码记录"></a>代码记录</h2>
                  <p>作为一名程序员，我们会经常写或者使用一些关键代码。</p>
                  <p>比如有一天我写了一些方法，这些方法可以完成非常重要的功能，后面的项目也会经常遇到，那么怎么办呢？很多情况下我们想把它保存起来，放到某个收藏夹里面备用，等到用的时候重新把它复制出来。或者有一些繁琐的命令，我实在是记不住，或许我们也想把它记录下来。</p>
                  <p>很多情况下，我们可能简单地使用文本文件，但并不方便同步和查找。或者云笔记保存下来，但这些并不是专门用来保存代码的。更高级一点，我们会联想到使用 GitHub Gists，但每次记录的这个流程也比较麻烦。</p>
                  <p>这里推荐一个专门用来记录代码片段的软件，叫做 SnippetsLab，适用于 Mac 系统，可以专门用来管理代码片段，还支持多种代码格式。比如我就将代码按照编程语言划分，划分为 Python、JavaScript 等等，分文件夹存储，有不错的代码就随手贴过来，另外它也支持搜索，管理代码非常方便。如果某一天想查某个代码了，直接打开它一搜就有了，可以大大提高开发效率。</p>
                  <p><img src="https://ws4.sinaimg.cn/large/006tNc79gy1g0579amrgyj31f70u0dq4.jpg" alt=""></p>
                  <p>以上就暂且总结这么多，其实还有不少好用的用具，后面再一一为大家总结分享。</p>
                  <p>另外再问下大家，你们买 iPad 了吗？是不是觉得比较鸡肋，或者平时都用不上，那这样就没有发挥 iPad 的最大效用，如果利用好了，它可以进一步方便我们的生活，后面我也会专门写一下 iPad 方面的一些用途。</p>
                  <p>由于水平和见识有限，如果大家有更好的软件或者方案推荐，欢迎大家留言！也希望我的一些方案对大家有所帮助，谢谢！</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-02-21 09:19:30" itemprop="dateCreated datePublished" datetime="2019-02-21T09:19:30+08:00">2019-02-21</time>
                </span>
                <span id="/6457.html" class="post-meta-item leancloud_visitors" data-flag-title="分享一套 MacBook 高效开发工具" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>5.7k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>5 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6454.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6454.html" class="post-title-link" itemprop="url">还在一个个写规则？来了解下爬虫的智能化解析吧！</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>爬虫是做什么的？是帮助我们来快速获取有效信息的。然而做过爬虫的人都知道，解析是个麻烦事。 比如一篇新闻吧，链接是这个：<a href="https://news.ifeng.com/c/7kQcQG2peWU，页面预览图如下：" target="_blank" rel="noopener">https://news.ifeng.com/c/7kQcQG2peWU，页面预览图如下：</a> <img src="https://ws2.sinaimg.cn/large/006tKfTcly1g0cwc0n5u9j30w10no4ho.jpg" alt="预览图"> 我们需要从页面中提取出标题、发布人、发布时间、发布内容、图片等内容。一般情况下我们需要怎么办？写规则。 那么规则都有什么呢？怼正则，怼 CSS 选择器，怼 XPath。我们需要对标题、发布时间、来源等内容做规则匹配，更有甚者再需要正则表达式来辅助一下。我们可能就需要用 re、BeautifulSoup、pyquery 等库来实现内容的提取和解析。 但如果我们有成千上万个不同样式的页面怎么办呢？它们来自成千上万个站点，难道我们还需要对他们一一写规则来匹配吗？这得要多大的工作量啊。另外这些万一弄不好还会解析有问题。比如正则表达式在某些情况下匹配不了了，CSS、XPath 选择器选错位了也会出现问题。 想必大家可能见过现在的浏览器有阅读模式，比如我们把这个页面用 Safari 浏览器打开，然后开启阅读模式，看看什么效果： <img src="https://ws4.sinaimg.cn/large/006tKfTcly1g0cwgg60joj30mg0odwtt.jpg" alt="Safari预览"> 页面一下子变得非常清爽，只保留了标题和需要读的内容。原先页面多余的导航栏、侧栏、评论等等的统统都被去除了。它怎么做到的？难道是有人在里面写好规则了？那当然不可能的事。其实，这里面就用到了智能化解析了。 那么本篇文章，我们就来了解一下页面的智能化解析的相关知识。</p>
                  <h2 id="智能化解析"><a href="#智能化解析" class="headerlink" title="智能化解析"></a>智能化解析</h2>
                  <p>所谓爬虫的智能化解析，顾名思义就是不再需要我们针对某一些页面来专门写提取规则了，我们可以利用一些算法来计算出来页面特定元素的位置和提取路径。比如一个页面中的一篇文章，我们可以通过算法计算出来，它的标题应该是什么，正文应该是哪部分区域，发布时间是什么等等。 其实智能化解析是非常难的一项任务，比如说你给人看一个网页的一篇文章，人可以迅速找到这篇文章的标题是什么，发布时间是什么，正文是哪一块，或者哪一块是广告位，哪一块是导航栏。但给机器来识别的话，它面临的是什么？仅仅是一系列的 HTML 代码而已。那究竟机器是怎么做到智能化提取的呢？其实这里面融合了多方面的信息。</p>
                  <ul>
                    <li>比如标题。一般它的字号是比较大的，而且长度不长，位置一般都在页面上方，而且大部分情况下它应该和 title 标签里的内容是一致的。</li>
                    <li>比如正文。它的内容一般是最多的，而且会包含多个段落 p 或者图片 img 标签，另外它的宽度一般可能会占用到页面的三分之二区域，并且密度（字数除以标签数量）会比较大。</li>
                    <li>比如时间。不同语言的页面可能不同，但时间的格式是有限的，如 2019-02-20 或者 2019/02/20 等等，也有的可能是美式的记法，顺序不同，这些也有特定的模式可以识别。</li>
                    <li>比如广告。它的标签一般可能会带有 ads 这样的字样，另外大多数可能会处于文章底部、页面侧栏，并可能包含一些特定的外链内容。</li>
                  </ul>
                  <p>另外还有一些特点就不再一一赘述了，这其中包含了区块位置、区块大小、区块标签、区块内容、区块疏密度等等多种特征，另外很多情况下还需要借助于视觉的特征，所以说这里面其实结合了算法计算、视觉处理、自然语言处理等各个方面的内容。如果能把这些特征综合运用起来，再经过大量的数据训练，是可以得到一个非常不错的效果的。</p>
                  <h2 id="业界进展"><a href="#业界进展" class="headerlink" title="业界进展"></a>业界进展</h2>
                  <p>未来的话，页面也会越来越多，页面的渲染方式也会发生很大的变化，爬虫也会越来越难做，智能化爬虫也将会变得越来越重要。 目前工业界，其实已经有落地的算法应用了。经过我的一番调研，目前发现有这么几种算法或者服务对页面的智能化解析做的比较好：</p>
                  <ul>
                    <li>Diffbot，国外的一家专门来做智能化解析服务的公司，<a href="https://www.diffbot.com" target="_blank" rel="noopener">https://www.diffbot.com</a></li>
                    <li>Boilerpipe，Java 语言编写的一个页面解析算法，<a href="https://github.com/kohlschutter/boilerpipe" target="_blank" rel="noopener">https://github.com/kohlschutter/boilerpipe</a></li>
                    <li>Embedly，提供页面解析服务的公司，<a href="https://embed.ly/extract" target="_blank" rel="noopener">https://embed.ly/extract</a></li>
                    <li>Readability，是一个页面解析算法，但现在官方的服务已经关闭了，<a href="https://www.readability.com/" target="_blank" rel="noopener">https://www.readability.com/</a></li>
                    <li>Mercury，Readability 的替代品，<a href="https://mercury.postlight.com/" target="_blank" rel="noopener">https://mercury.postlight.com/</a></li>
                    <li>Goose，Java 语音编写的页面解析算法，<a href="https://github.com/GravityLabs/goose" target="_blank" rel="noopener">https://github.com/GravityLabs/goose</a></li>
                  </ul>
                  <p>那么这几种算法或者服务到底哪些好呢，Driffbot 官方曾做过一个对比评测，使用 Google 新闻的一些文章，使用不同的算法依次摘出其中的标题和文本，然后与真实标注的内容进行比较，比较的指标就是文字的准确率和召回率，以及根据二者计算出的 F1 分数。 其结果对比如下：</p>
                  <p>Service/Software</p>
                  <p><a href="https://www.diffbot.com/benefits/comparison/#definitions" target="_blank" rel="noopener">Precision</a></p>
                  <p><a href="https://www.diffbot.com/benefits/comparison/#definitions" target="_blank" rel="noopener">Recall</a></p>
                  <p><a href="https://www.diffbot.com/benefits/comparison/#definitions" target="_blank" rel="noopener">F1-Score</a></p>
                  <p><strong>Diffbot</strong></p>
                  <p>0.968</p>
                  <p>0.978</p>
                  <p>0.971</p>
                  <p><a href="https://code.google.com/p/boilerpipe/" target="_blank" rel="noopener">Boilerpipe</a></p>
                  <p>0.893</p>
                  <p>0.924</p>
                  <p>0.893</p>
                  <p><a href="https://www.readability.com/" target="_blank" rel="noopener">Readability</a></p>
                  <p>0.819</p>
                  <p>0.911</p>
                  <p>0.854</p>
                  <p><a href="http://www.alchemyapi.com/" target="_blank" rel="noopener">AlchemyAPI</a></p>
                  <p>0.876</p>
                  <p>0.892</p>
                  <p>0.850</p>
                  <p><a href="http://embed.ly/" target="_blank" rel="noopener">Embedly</a></p>
                  <p>0.786</p>
                  <p>0.880</p>
                  <p>0.822</p>
                  <p><a href="https://github.com/GravityLabs/goose" target="_blank" rel="noopener">Goose</a></p>
                  <p>0.498</p>
                  <p>0.815</p>
                  <p>0.608</p>
                  <p>经过对比我们可以发现，Diffbot 的准确率和召回率都独占鳌头，其中的 F1 值达到了 0.97，可以说准确率非常高了。另外接下来比较厉害的就是 Boilerpipe 和 Readability，Goose 的表现则非常差，F1 跟其他的算法差了一大截。下面是几个算法的 F1 分数对比情况： <img src="https://ws2.sinaimg.cn/large/006tKfTcgy1g0d4cv31noj30yw0kstaa.jpg" alt="F1分数对比"> 有人可能好奇为什么 Diffbot 这么厉害？我也查询了一番。Diffbot 自 2010 年以来就致力于提取 Web 页面数据，并提供许多 API 来自动解析各种页面。其中他们的算法依赖于自然语言技术、机器学习、计算机视觉、标记检查等多种算法，并且所有的页面都会考虑到当前页面的样式以及可视化布局，另外还会分析其中包含的图像内容、CSS 甚至 Ajax 请求。另外在计算一个区块的置信度时还考虑到了和其他区块的关联关系，基于周围的标记来计算每个区块的置信度。 总之，Diffbot 也是一直致力于这一方面的服务，整个 Diffbot 就是页面解析起家的，现在也一直专注于页面解析服务，准确率高也就不足为怪了。 但它们的算法开源了吗？很遗憾，并没有，而且我也没有找到相关的论文介绍它们自己的具体算法。 所以，如果想实现这么好的效果，那就使用它们家的服务就好了。 接下来的内容，我们就来说说如何使用 Diffbot 来进行页面的智能解析。另外还有 Readability 算法也非常值得研究，我会写专门的文章来介绍 Readability 及其与 Python 的对接使用。</p>
                  <h2 id="Diffbot-页面解析"><a href="#Diffbot-页面解析" class="headerlink" title="Diffbot 页面解析"></a>Diffbot 页面解析</h2>
                  <p>首先我们需要注册一个账号，它有 15 天的免费试用，注册之后会获得一个 Developer Token，这就是使用 Diffbot 接口服务的凭证。 接下来切换到它的测试页面中，链接为：<a href="https://www.diffbot.com/dev/home/，我们来测试一下它的解析效果到底是怎样的。" target="_blank" rel="noopener">https://www.diffbot.com/dev/home/，我们来测试一下它的解析效果到底是怎样的。</a> 这里我们选择的测试页面就是上文所述的页面，链接为：<a href="https://news.ifeng.com/c/7kQcQG2peWU，API" target="_blank" rel="noopener">https://news.ifeng.com/c/7kQcQG2peWU，API</a> 类型选择 Article API，然后点击 Test Drive 按钮，接下来它就会出现当前页面的解析结果： <img src="https://ws1.sinaimg.cn/large/006tKfTcgy1g0d4u30pjij30v70lhq57.jpg" alt="结果"> 这时候我们可以看到，它帮我们提取出来了标题、发布时间、发布机构、发布机构链接、正文内容等等各种结果。而且目前来看都十分正确，时间也自动识别之后做了转码，是一个标准的时间格式。 接下来我们继续下滑，查看还有什么其他的字段，这里我们还可以看到有 html 字段，它和 text 不同的是，它包含了文章内容的真实 HTML 代码，因此图片也会包含在里面，如图所示： <img src="https://ws1.sinaimg.cn/large/006tKfTcgy1g0d4wnlzbhj30uv0k3490.jpg" alt="结果"> 另外最后面还有 images 字段，他以列表形式返回了文章套图及每一张图的链接，另外还有文章的站点名称、页面所用语言等等结果，如图所示： <img src="https://ws2.sinaimg.cn/large/006tKfTcgy1g0d4yai8qqj30uf0l0q7v.jpg" alt="结果"> 当然我们也可以选择 JSON 格式的返回结果，其内容会更加丰富，例如图片还返回了其宽度、高度、图片描述等等内容，另外还有各种其他的结果如面包屑导航等等结果，如图所示： <img src="https://ws3.sinaimg.cn/large/006tKfTcgy1g0d500i671j30tn0fx0ue.jpg" alt="结果"> 经过手工核对，发现其返回的结果都是完全正确的，准确率相当之高！ 所以说，如果你对准确率要求没有那么非常非常严苛的情况下，使用 Diffbot 的服务可以帮助我们快速地提取页面中所需的结果，省去了我们绝大多数的手工劳动，可以说是非常赞了。 但是，我们也不能总在网页上这么试吧。其实 Diffbot 也提供了官方的 API 文档，让我们来一探究竟。</p>
                  <h2 id="Diffbot-API"><a href="#Diffbot-API" class="headerlink" title="Diffbot API"></a>Diffbot API</h2>
                  <p>Driffbot 提供了多种 API，如 Analyze API、Article API、Disscussion API 等。 下面我们以 Article API 为例来说明一下它的用法，其官方文档地址为：<a href="https://www.diffbot.com/dev/docs/article/，API" target="_blank" rel="noopener">https://www.diffbot.com/dev/docs/article/，API</a> 调用地址为：</p>
                  <figure class="highlight awk">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">https:<span class="regexp">//</span>api.diffbot.com<span class="regexp">/v3/</span>article</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>我们可以用 GET 方式来进行请求，其中的 Token 和 URL 都可以以参数形式传递给这个 API，其必备的参数有：</p>
                  <ul>
                    <li>token：即 Developer Token</li>
                    <li>url：即要解析的 URL 链接</li>
                  </ul>
                  <p>另外它还有几个可选参数：</p>
                  <ul>
                    <li>fields：用来指定返回哪些字段，默认已经有了一些固定字段，这个参数可以指定还可以额外返回哪些可选字段</li>
                    <li>paging：如果是多页文章的话，如果将这个参数设置为 false 则可以禁止多页内容拼接</li>
                    <li>maxTags：可以设置返回的 Tag 最大数量，默认是 10 个</li>
                    <li>tagConfidence：设置置信度的阈值，超过这个值的 Tag 才会被返回，默认是 0.5</li>
                    <li>discussion：如果将这个参数设置为 false，那么就不会解析评论内容</li>
                    <li>timeout：在解析的时候等待的最长时间，默认是 30 秒</li>
                    <li>callback：为 JSONP 类型的请求而设计的回调</li>
                  </ul>
                  <p>这里大家可能关注的就是 fields 字段了，在这里我专门做了一下梳理，首先是一些固定字段：</p>
                  <ul>
                    <li>type：文本的类型，这里就是 article 了</li>
                    <li>title：文章的标题</li>
                    <li>text：文章的纯文本内容，如果是分段内容，那么其中会以换行符来分隔</li>
                    <li>html：提取结果的 HTML 内容</li>
                    <li>date：文章的发布时间，其格式为 RFC 1123</li>
                    <li>estimatedDate：如果日期时间不太明确，会返回一个预估的时间，如果文章超过两天或者没有发布日期，那么这个字段就不会返回</li>
                    <li>author：作者</li>
                    <li>authorUrl：作者的链接</li>
                    <li>discussion：评论内容，和 Disscussion API 返回结果一样</li>
                    <li>humanLanguage：语言类型，如英文还是中文等</li>
                    <li>numPages：如果文章是多页的，这个参数会控制最大的翻页拼接数目</li>
                    <li>nextPages：如果文章是多页的，这个参数可以指定文章后续链接</li>
                    <li>siteName：站点名称</li>
                    <li>publisherRegion：文章发布地区</li>
                    <li>publisherCountry：文章发布国家</li>
                    <li>pageUrl：文章链接</li>
                    <li>resolvedPageUrl：如果文章是从 pageUrl 重定向过来的，则返回此内容</li>
                    <li>tags：文章的标签或者文章包含的实体，根据自然语言处理技术和 DBpedia 计算生成，是一个列表，里面又包含了子字段：<ul>
                        <li>label：标签名</li>
                        <li>count：标签出现的次数</li>
                        <li>score：标签置信度</li>
                        <li>rdfTypes：如果实体可以由多个资源表示，那么则返回相关的 URL</li>
                        <li>type：类型</li>
                        <li>uri：Diffbot Knowledge Graph 中的实体链接</li>
                      </ul>
                    </li>
                    <li>images：文章中包含的图片</li>
                    <li>videos：文章中包含的视频</li>
                    <li>breadcrumb：面包屑导航信息</li>
                    <li>diffbotUri：Diffbot 内部的 URL 链接</li>
                  </ul>
                  <p>以上的预定字段就是如果可以返回那就会返回的字段，是不能定制化配置的，另外我们还可以通过 fields 参数来指定扩展如下可选字段：</p>
                  <ul>
                    <li>quotes：引用信息</li>
                    <li>sentiment：文章的情感值，-1 到 1 之间</li>
                    <li>links：所有超链接的顶级链接</li>
                    <li>querystring：请求的参数列表</li>
                  </ul>
                  <p>好，以上便是这个 API 的用法，大家可以申请之后使用这个 API 来做智能化解析了。 下面我们用一个实例来看一下这个 API 的用法，代码如下：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">import requests, json</span><br><span class="line"></span><br><span class="line">url = <span class="string">'https://api.diffbot.com/v3/article'</span></span><br><span class="line">params = &#123;</span><br><span class="line">    <span class="string">'token'</span>: <span class="string">'77b41f6fbb24496d5113d528306528fa'</span>,</span><br><span class="line">    <span class="string">'url'</span>: <span class="string">'https://news.ifeng.com/c/7kQcQG2peWU'</span>,</span><br><span class="line">    <span class="string">'fields'</span>: <span class="string">'meta'</span> </span><br><span class="line">&#125;</span><br><span class="line">response = requests.<span class="builtin-name">get</span>(url, <span class="attribute">params</span>=params)</span><br><span class="line"><span class="builtin-name">print</span>(json.dumps(response.json(), <span class="attribute">indent</span>=2, <span class="attribute">ensure_ascii</span>=<span class="literal">False</span>))</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>这里首先定义了 API 的链接，然后指定了 params 参数，即 GET 请求参数。 参数中包含了必选的 token、url 字段，也设置了可选的 fields 字段，其中 fields 为可选的扩展字段 meta 标签。 我们来看下运行结果，结果如下：</p>
                  <figure class="highlight json">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"request"</span>: &#123;</span><br><span class="line">    <span class="attr">"pageUrl"</span>: <span class="string">"https://news.ifeng.com/c/7kQcQG2peWU"</span>,</span><br><span class="line">    <span class="attr">"api"</span>: <span class="string">"article"</span>,</span><br><span class="line">    <span class="attr">"fields"</span>: <span class="string">"sentiment, meta"</span>,</span><br><span class="line">    <span class="attr">"version"</span>: <span class="number">3</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">"objects"</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">"date"</span>: <span class="string">"Wed, 20 Feb 2019 02:26:00 GMT"</span>,</span><br><span class="line">      <span class="attr">"images"</span>: [</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">"naturalHeight"</span>: <span class="number">460</span>,</span><br><span class="line">          <span class="attr">"width"</span>: <span class="number">640</span>,</span><br><span class="line">          <span class="attr">"diffbotUri"</span>: <span class="string">"image|3|-1139316034"</span>,</span><br><span class="line">          <span class="attr">"url"</span>: <span class="string">"http://e0.ifengimg.com/02/2019/0219/1731DC8A29EB2219C7F2773CF9CF319B3503D0A1_size382_w690_h460.png"</span>,</span><br><span class="line">          <span class="attr">"naturalWidth"</span>: <span class="number">690</span>,</span><br><span class="line">          <span class="attr">"primary"</span>: <span class="literal">true</span>,</span><br><span class="line">          <span class="attr">"height"</span>: <span class="number">426</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">      ],</span><br><span class="line">      <span class="attr">"author"</span>: <span class="string">"中国新闻网"</span>,</span><br><span class="line">      <span class="attr">"estimatedDate"</span>: <span class="string">"Wed, 20 Feb 2019 06:47:52 GMT"</span>,</span><br><span class="line">      <span class="attr">"diffbotUri"</span>: <span class="string">"article|3|1591137208"</span>,</span><br><span class="line">      <span class="attr">"siteName"</span>: <span class="string">"ifeng.com"</span>,</span><br><span class="line">      <span class="attr">"type"</span>: <span class="string">"article"</span>,</span><br><span class="line">      <span class="attr">"title"</span>: <span class="string">"故宫，你低调点！故宫：不，实力已不允许我继续低调"</span>,</span><br><span class="line">      <span class="attr">"breadcrumb"</span>: [</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">"link"</span>: <span class="string">"https://news.ifeng.com/"</span>,</span><br><span class="line">          <span class="attr">"name"</span>: <span class="string">"资讯"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">"link"</span>: <span class="string">"https://news.ifeng.com/shanklist/3-35197-/"</span>,</span><br><span class="line">          <span class="attr">"name"</span>: <span class="string">"大陆"</span></span><br><span class="line">        &#125;</span><br><span class="line">      ],</span><br><span class="line">      <span class="attr">"humanLanguage"</span>: <span class="string">"zh"</span>,</span><br><span class="line">      <span class="attr">"meta"</span>: &#123;</span><br><span class="line">        <span class="attr">"og"</span>: &#123;</span><br><span class="line">          <span class="attr">"og:time "</span>: <span class="string">"2019-02-20 02:26:00"</span>,</span><br><span class="line">          <span class="attr">"og:image"</span>: <span class="string">"https://e0.ifengimg.com/02/2019/0219/1731DC8A29EB2219C7F2773CF9CF319B3503D0A1_size382_w690_h460.png"</span>,</span><br><span class="line">          <span class="attr">"og:category "</span>: <span class="string">"凤凰资讯"</span>,</span><br><span class="line">          <span class="attr">"og: webtype"</span>: <span class="string">"news"</span>,</span><br><span class="line">          <span class="attr">"og:title"</span>: <span class="string">"故宫，你低调点！故宫：不，实力已不允许我继续低调"</span>,</span><br><span class="line">          <span class="attr">"og:url"</span>: <span class="string">"https://news.ifeng.com/c/7kQcQG2peWU"</span>,</span><br><span class="line">          <span class="attr">"og:description"</span>: <span class="string">"　　“我的名字叫紫禁城，快要600岁了，这上元的夜啊，总是让我沉醉，这么久了却从未停止。” 　　“重"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">"referrer"</span>: <span class="string">"always"</span>,</span><br><span class="line">        <span class="attr">"description"</span>: <span class="string">"　　“我的名字叫紫禁城，快要600岁了，这上元的夜啊，总是让我沉醉，这么久了却从未停止。” 　　“重"</span>,</span><br><span class="line">        <span class="attr">"keywords"</span>: <span class="string">"故宫 紫禁城 故宫博物院 灯光 元宵节 博物馆 一票难求 元之 中新社 午门 杜洋 藏品 文化 皇帝 清明上河图 元宵 千里江山图卷 中英北京条约 中法北京条约 天津条约"</span>,</span><br><span class="line">        <span class="attr">"title"</span>: <span class="string">"故宫，你低调点！故宫：不，实力已不允许我继续低调_凤凰资讯"</span></span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="attr">"authorUrl"</span>: <span class="string">"https://feng.ifeng.com/author/308904"</span>,</span><br><span class="line">      <span class="attr">"pageUrl"</span>: <span class="string">"https://news.ifeng.com/c/7kQcQG2peWU"</span>,</span><br><span class="line">      <span class="attr">"html"</span>: <span class="string">"&lt;p&gt;“我的名字叫紫禁城，快要600岁了，这上元的夜啊，总是让我沉醉，这么久了却从未停止。...&lt;/blockquote&gt; &lt;/blockquote&gt;"</span>,</span><br><span class="line">      <span class="attr">"text"</span>: <span class="string">"“我的名字叫紫禁城，快要600岁了，这上元的夜啊，总是让我沉醉，这么久了却从未停止。”\\n“..."</span>,</span><br><span class="line">      <span class="attr">"authors"</span>: [</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">"name"</span>: <span class="string">"中国新闻网"</span>,</span><br><span class="line">          <span class="attr">"link"</span>: <span class="string">"https://feng.ifeng.com/author/308904"</span></span><br><span class="line">        &#125;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>可见其返回了如上的内容，是一个完整的 JSON 格式，其中包含了标题、正文、发布时间等等各种内容。 可见，不需要我们配置任何提取规则，我们就可以完成页面的分析和抓取，得来全不费功夫。</p>
                  <h2 id="Diffbot-SDK"><a href="#Diffbot-SDK" class="headerlink" title="Diffbot SDK"></a>Diffbot SDK</h2>
                  <p>另外 Diffbot 还提供了几乎所有语言的 SDK 支持，我们也可以使用 SDK 来实现如上功能，链接为：<a href="https://www.diffbot.com/dev/docs/libraries/，如果大家使用" target="_blank" rel="noopener">https://www.diffbot.com/dev/docs/libraries/，如果大家使用</a> Python 的话，可以直接使用 Python 的 SDK 即可，Python 的 SDK 链接为：<a href="https://github.com/diffbot/diffbot-python-client。" target="_blank" rel="noopener">https://github.com/diffbot/diffbot-python-client。</a> 这个库并没有发布到 PyPi，需要自己下载并导入使用，另外这个库是使用 Python 2 写的，其实本质上就是调用了 requests 库，如果大家感兴趣的话可以看一下。 下面是一个调用示例：</p>
                  <figure class="highlight routeros">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">from</span><span class="built_in"> client </span>import DiffbotClient,DiffbotCrawl</span><br><span class="line"></span><br><span class="line">diffbot = DiffbotClient()</span><br><span class="line">token = <span class="string">'your_token'</span></span><br><span class="line">url = <span class="string">'http://shichuan.github.io/javascript-patterns/'</span></span><br><span class="line">api = <span class="string">'article'</span></span><br><span class="line">response = diffbot.request(url, token, api)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>通过这行代码我们就可以通过调用 Article API 来分析我们想要的 URL 链接了，返回结果是类似的。 具体的用法大家直接看下它的源码注释就一目了然了，还是很清楚的。 好，以上便是对智能化提取页面原理的基本介绍以及对 Diffbot 的用法的讲解，后面我会继续介绍其他的智能化解析方法以及一些相关实战，希望大家可以多多关注。</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-02-21 09:17:06" itemprop="dateCreated datePublished" datetime="2019-02-21T09:17:06+08:00">2019-02-21</time>
                </span>
                <span id="/6454.html" class="post-meta-item leancloud_visitors" data-flag-title="还在一个个写规则？来了解下爬虫的智能化解析吧！" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>7.8k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>7 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6431.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6431.html" class="post-title-link" itemprop="url">今天，我终于弄懂了字体反爬是个啥玩意！</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <h1 id="大家好，我是四毛，欢迎关注我的公众号。"><a href="#大家好，我是四毛，欢迎关注我的公众号。" class="headerlink" title="大家好，我是四毛，欢迎关注我的公众号。"></a>大家好，我是四毛，欢迎关注我的公众号。</h1>
                  <h1 id="有什么想要交流的可以在后台第一时间私我。"><a href="#有什么想要交流的可以在后台第一时间私我。" class="headerlink" title="有什么想要交流的可以在后台第一时间私我。"></a>有什么想要交流的可以在后台第一时间私我。</h1>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/201802111155445124jhQyer.yasuotu.gif" alt=""></p>
                  <h2 id="今天的文章内容主要是关于字体反爬。"><a href="#今天的文章内容主要是关于字体反爬。" class="headerlink" title="今天的文章内容主要是关于字体反爬。"></a>今天的文章内容主要是关于字体反爬。</h2>
                  <p>目前已知的几个字体反爬的网站是猫眼，汽车之家，天眼查，起点中文网等等。 以前也看过这方面的文章，今天跟个老哥在交流的时候，终于实操了一把，弄懂了字体反爬是个啥玩意。下面听我慢慢道来。</p>
                  <h3 id="本文用到的第三方库"><a href="#本文用到的第三方库" class="headerlink" title="本文用到的第三方库"></a><strong>本文用到的第三方库</strong></h3>
                  <blockquote>
                    <p><strong>fontTools</strong></p>
                  </blockquote>
                  <h2 id="1、目标网站"><a href="#1、目标网站" class="headerlink" title="1、目标网站"></a><strong>1、目标网站</strong></h2>
                  <blockquote>
                    <p><strong>url = “<a href="https://su.58.com/qztech/" target="_blank" rel="noopener">https://su.58.com/qztech/</a>“</strong></p>
                  </blockquote>
                  <h2 id="2、反爬虫机制"><a href="#2、反爬虫机制" class="headerlink" title="2、反爬虫机制"></a><strong>2、反爬虫机制</strong></h2>
                  <p>网页上看见的 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/1.png" alt=""> 后台源代码里面的 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/2.png" alt=""> 从上面可以看出，生这个字变成了乱码，请大家特别注意箭头所指的数字。 </p>
                  <h2 id="3、解决"><a href="#3、解决" class="headerlink" title="3、解决"></a><strong>3、解决</strong></h2>
                  <h3 id="1、确定反爬方法"><a href="#1、确定反爬方法" class="headerlink" title="1、确定反爬方法"></a><strong>1、确定反爬方法</strong></h3>
                  <p>在看了别人的解析文章之后，确定采取的是<strong>字体反爬机制</strong>，即网站定义了字体文件，然后进行相应的查找替换，在前端看起来，是没有任何差异的。其实从审查元素的也是可以看到的： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/4.png" alt=""> 和大众点评的反爬差不多，都是通过css搞得。</p>
                  <h3 id="2、寻找字体文件"><a href="#2、寻找字体文件" class="headerlink" title="2、寻找字体文件"></a><strong>2、寻找字体文件</strong></h3>
                  <p>以上面方框里的”<strong>customfont</strong>“为关键词搜了一下，发现就在源代码里面： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/3.png" alt=""> 而且还有base64，直接进行解密，但是解密出来的其实是乱码，这个时候其实要做的很简单，把解密后的内容保存为<strong>.ttf</strong>格式即可。</p>
                  <blockquote>
                    <p><strong>ttf文件</strong>： <em>.ttf是字体文件格式。TTF(TrueTypeFont)是Apple公司和Microsoft公司共同推出的字体文件格式,随着windows的流行,已经变成最常用的一种字体文件表示方式。 <em>*@font-face</em></em> 是CSS3中的一个模块，主要是实现将自定义的Web字体嵌入到指定网页中去。</p>
                  </blockquote>
                  <p>因为我们要对字体进行研究，所以必须将它打开，这里我是用的是<strong>FontCreator</strong>，打开以后是这个样子（其实很多字，在这里为了看的清楚，所以只截了下面的图）： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/5.png" alt=""> 很明显，每个字可以看到字形和字形编码。 观察现在箭头指的地方和前面箭头指的地方的数字是不是一样啊，没错，就是通过这种方法进行映射的。 所以我们现在的思路似乎就是在源代码里找到箭头指的数字，然后再来字体里找到后替换就行了。 恭喜你，如果你也是这么想的，那你就掉坑里了。 因为每次访问，字体字形是不变的，但字符的编码确是变化的。因此，我们需要根据每次访问，<strong>动态解析字体文件</strong>。 字体1： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/5.png" alt=""> 字体2： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/8.png" alt=""> 所以想通过写死的方式也是行不通的。 这个时候我们就要对字体文件进行更深一步的研究了。 <strong>3、研究字体文件</strong> 刚刚的.ttf文件我们是看不到内部的东西的，所以这个时候我们要对字体文件进行转换格式，将其转换为xml格式，然后来查看： 具体操作如下：</p>
                  <figure class="highlight reasonml">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">from fontTools.ttLib import TTFont</span><br><span class="line">font_1 = <span class="constructor">TTFont('58_font_1.<span class="params">ttf</span>')</span></span><br><span class="line">font_base.save<span class="constructor">XML('<span class="params">font_1</span>.<span class="params">xml</span>')</span></span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p>xml的格式如下： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/9-1.png" alt=""> <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/10.png" alt=""> 文件很长，我只截取了一部分。 <strong>仔细的观察一下，你会发现~这俩下面的x,y,on值都是一毛一样的。所以我们的思路就是以一个已知的字体文件为基本，然后将获取到的新的字体文件的每个文字对应的x,y,on值进行比较，如果相同，那么说明新的文字对就 可以在基础字体那里找到对应的文字，有点绕，下面举个小例子。</strong> <strong>假设: “我” 在基本字体中的名为uni1，对应的x=1,y=1,n=1新的字体文件中，一个名为uni2对应的x，y, n分别于上面的相等，那么这个时候就可以确定uni2 对应的文字为”我”。</strong> <strong>查资料的时候，发现在特殊情况下，有时候两个字体中的文字对应的x,y不相等，但是差距都是在某一个阈值之内，处理方法差不多，只不过上面是相等，这种情况下就是要比较一下。</strong> 其实，如果你用画图工具按照上面的x与y值把点给连起来，你会发现，就是汉字的字形~ 所以，到此总结一下：</p>
                  <blockquote>
                    <p><strong>一、将某次请求获取到的字体文件保存到本地[基本字体]；</strong> <strong>二、用软件打开后，人工的找出每一个数字对应的编码[</strong> <strong>一定要保证顺序的正确，要不然会出事]；</strong> <strong>三、我们以后访问网页时，需要保存新字体文件；</strong> <strong>四、用Fonttools库对基本字体与新字体进行处理，找</strong> <strong>到新的字体与基本字体之间的映射；</strong> <strong>五、替换；</strong></p>
                  </blockquote>
                  <h2 id="4、上代码"><a href="#4、上代码" class="headerlink" title="4、上代码"></a><strong>4、上代码</strong></h2>
                  <p>微信里上代码真的太丑了， 还是算了吧，微信后台关键词“<strong>字体加密</strong>” 即可获取github地址。 看一下成果 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/02/11.png" alt=""> </p>
                  <h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a><strong>总结</strong></h2>
                  <p>其实这个流程最大的问题就是我们人工录入的基本字体的字典数据有可能是会发生变化的，这就导致我们后面还要手动去改。 现在，如果你已经看懂了本文，那么还不快去其他几个网站试试？ 如果有任何问题，欢迎交流。 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/201802111155445124jhQyer.yasuotu.gif" alt=""></p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/四毛" class="author" itemprop="url" rel="index">四毛</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-02-20 21:22:24" itemprop="dateCreated datePublished" datetime="2019-02-20T21:22:24+08:00">2019-02-20</time>
                </span>
                <span id="/6431.html" class="post-meta-item leancloud_visitors" data-flag-title="今天，我终于弄懂了字体反爬是个啥玩意！" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>1.8k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>2 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6408.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6408.html" class="post-title-link" itemprop="url">第一次见到这种反爬虫。咋办？盘它。</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <h1 id="大家好，我是四毛，欢迎大家关注我的公众号。"><a href="#大家好，我是四毛，欢迎大家关注我的公众号。" class="headerlink" title="大家好，我是四毛，欢迎大家关注我的公众号。"></a>大家好，我是四毛，欢迎大家关注我的公众号。</h1>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/201802111155445124jhQyer.yasuotu.gif" alt=""> 今天在工作中，碰到了第一次碰见的反爬虫机制，感觉很有意思，在这里记录一下，希望对大家有帮助。</p>
                  <h2 id="今天用到的库："><a href="#今天用到的库：" class="headerlink" title="今天用到的库："></a><strong>今天用到的库</strong>：</h2>
                  <blockquote>
                    <p>requests [请求] lzstring [解压数据] pyexecjs [执行JS]</p>
                  </blockquote>
                  <p> 简单粗暴，直接上网站部分源代码，因为这个网站应该不太希望别人来爬，所以就不上网站了。为什么这么说，因为刚开始请求的时候，老是给我返回GO TO HELL ，哈哈。 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2016/12/兔子.gif" alt=""> 这个网站点击鼠标右键审查元素，查看网页源代码是无法用的，但是这个好像只能防住小白啊，<strong>简单的按F12审查元素，CTRL+u 直接查看源代码（谷歌浏览器）</strong>。 这次的目的主要是为了获取下面的链接（重度打码）</p>
                  <blockquote>
                    <p>xxxxxxxxxxx/<strong>xxxxx-003-a5f7xxxxxx</strong>?cid=xxxxx&amp;xxx=<strong>siOE_q4XxBtwdoXqD0xxxx</strong></p>
                  </blockquote>
                  <p>其中，红色加粗的就是我们要找的变量了。</p>
                  <h2 id="一、观察与抓包"><a href="#一、观察与抓包" class="headerlink" title="一、观察与抓包"></a>一、观察与抓包</h2>
                  <p><strong>首先</strong>，我观察到了网页源代码中的一部分js代码：</p>
                  <blockquote>
                    <p>type=”text/javascript”&gt;window<a href="function(p,a,c,k,e,d">“\x65\x76\x61\x6c”</a>{e=function(c){return(c<a?"":e(parseInt(c /a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!’’.replace(/^/,String)){while(c—)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return’\\w+’};c=1;};while(c—)if(k[c])p=p.replace(new RegExp(‘\\b’+e(c)+’\\b’,’g’),k[c]);return p;}(‘29.2a({“2c”:1k,”26”:”21”,”22”:”1k.24”,”2o”:2n,”2s”:”2r”,”2l”:[“0-2f-2e.3.2”,”0-2j-2i.3.2”,”0-1r-1L.3.2”,”0-1r-1K.3.2”,”0-1e-1X.3.2”,”0-1e-1Q.3.2”,”0-1x-1W.3.2”,”0-1x-1M.3.2”,”0-1B-33.3.2”,”0-1B-36.3.2”,”0-1F-30.3.2”,”0-1F-2U.3.2”,”0-1g-2Y.3.2”,”0-1g-3e.3.2”,”0-1q-3i.3.2”,”0-1q-38.3.2”,”0-1o-37.3.2”,”0-1o-3a.3.2”,”0-1s-2B.3.2”,”0-1s-2E.3.2”,”0-1i-2v.3.2”,”0-1i-2y.3.2”,”0-1G-2P.3.2”,”0-1G-2S.3.2”,”0-1E-2R.3.2”,”0-1E-2H.3.2”,”0-1I-2L.3.2”,”0-1I-2J.3.2”,”0-t-2K.3.2”,”0-t-2G.3.2”,”0-p-2I.3.2”,”0-p-2M.3.2”,”0-o-2Q.3.2”,”0-o-2N.3.2”,”0-u-2O.3.2”,”0-u-2w.3.2”,”0-D-2x.3.2”,”0-D-2t.3.2”,”0-z-2u.3.2”,”0-z-2z.3.2”,”0-y-2D.3.2”,”0-y-2F.3.2”,”0-7-2A.3.2”,”0-7-2C.3.2”,”0-5-2T.3.2”,”0-5-3b.3.2”,”0-4-3c.3.2”,”0-4-39.3.2”,”0-g-3d.3.2”,”0-g-3h.3.2”,”0-i-3j.3.2”,”0-i-3f.3.2”,”0-b-3g.3.2”,”0-b-2X.3.2”,”0-d-2Z.3.2”,”0-d-2V.3.2”,”0-11-2W.3.2”,”0-11-34.3.2”,”0-13-35.3.2”,”0-13-31.3.2”,”0-10-32.3.2”,”0-10-1J.3.2”,”0-15-1O.3.2”,”0-15-1P.3.2”,”0-1d-1R.3.2”,……………………………………,’M4UxFsAsB9odxAIwA7WQOwObQAwCYBWXPAFlwHYBmY6ncousuvYgDlwDZ38BOT8zn3wcKARmICKOXK1qsmrFjlHdRAnKxEb1rBqK2ih5dSSbkteJXnEbpGm+SF47HLZSEElBOwRsEmlOqU3AS0JHYkWiRKJAwkYTYkQiQmIQwcShy0HDaUSpQuDJQ2HAG0BEFFWgTVQhw+3JRMOEw8Shq4PK……………………………………..GHXTzLjxjzN5CziB+3ODSAhicwuCyeBJ3YJD+eKhEi83fHm76PmK3Y/htPJFzw8LzOCqKTMcXGHr7Jm/nlPtuSUfPBAA===’<a href="&#39;\\x7c&#39;">‘\x73\x70\x6c\x69\x63’</a>,0,{})) &lt;/script&gt;</p>
                  </blockquote>
                  <p>为了节省篇幅，我把一些替换成…….了。如果你对这些数据的解压有兴趣，请后台联系我。 我第一眼看见时，做了2件事： 1、把[\x65\x76\x61\x6c]，[ \x73\x70\x6c\x69\x63]，[\x7c]分别解码，解码出的结果为eval和splic，| ； <strong>注：</strong>其实这里看到eval时就应该想到可以试试直接用pyexecjs执行后面的那段js匿名函数，但是当时确实啥都没有想起来，很惭愧，后来复盘时才发现这一点。虽然执行了也会报错。 2、把那一长串的字母试着用base64解码了，但是解不出来； <strong>然后，</strong>就只能再去其他js文件里找了，也找到了js代码，打了断点，但是看着还是很烦，于是这个时候我就没有在js上面死抠。 <strong>接着</strong>，我调转了方向，在GITHUB，Google运用了我祖传的高级搜索技巧，Finally，终于可以盘它了。</p>
                  <h2 id="二、解密"><a href="#二、解密" class="headerlink" title="二、解密"></a>二、解密</h2>
                  <p>下面开始解密： 1、<strong>数据解压</strong> 包含A===的字符串到底时啥呢，其实<strong>这个是js的一种数据的压缩方式</strong>，知道了这种方式你就可以立即破解这种反爬虫机制了，反正我以前是不知道的，第一次见到，学习了。 在这里，lzstring闪亮登场，运用这个库，执行下面的代码：</p>
                  <blockquote>
                    <p>lzstring.LZString().decompressFromBase64(string)</p>
                  </blockquote>
                  <p>这样就可以把上面的那串字符给解码了，解出来的数据像下面这样：</p>
                  <blockquote>
                    <p><strong>webp|png|025|024|073</strong></p>
                  </blockquote>
                  <p>没错，数据解压就是这么的简单； 2、<strong>看懂JS</strong> 我们在上面解码出来了一个splic和一个[ | ]，再看看我们上面解压出来的数据，是不是很有感觉；但是，查了好一会资料，也没有发现js里面有这个方法，可能是在别的地方定义了；在我找到的资料里面，那个作者是直接用了split，最后的结果是对的，走逻辑上也说得过去，不知道为什么这么设置成splic，还希望有大神可以指教； 3、<strong>执行JS</strong> 所以，到这里就很明显了，把解码以后以及解压以后的数据在替换会原js数据中，我们前面解码出来的eval就有用了，我找到的资料里面作者使用node做的，我没有安装node环境，所以我就直接用了pyexecjs.eval直接执行了，结果也正确； 4、<strong>梳理流程</strong>： 匹配长字符串==&gt;lzstring解压+解码后的字段==&gt;拼凑成新的js代码==&gt;pyexecjs执行==&gt;出结果 5、<strong>部分代码</strong> 执行的主要步骤和结果</p>
                  <blockquote>
                    <p>execjs.eval(res)</p>
                  </blockquote>
                  <p>没错，就是这么简单，res就是替换后的js，然后就可以直接出来我们上面网址中需要的两个字段了。 截个图吧 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/01/2.png" alt=""> <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2019/01/3.png" alt=""> </p>
                  <h2 id="三、结语"><a href="#三、结语" class="headerlink" title="三、结语"></a>三、结语</h2>
                  <p>到这里，今天的文章就结束了，总结一下就是<strong>知道了一种JS数据的压缩方式，并且学习了解压的方式，JS的执行，同时写代码的时候**</strong>多观察，多想，多试**。 如果觉得还不错，欢迎动动手指关注我。 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/201802111155445124jhQyer.yasuotu.gif" alt=""></p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/四毛" class="author" itemprop="url" rel="index">四毛</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-01-26 18:49:29" itemprop="dateCreated datePublished" datetime="2019-01-26T18:49:29+08:00">2019-01-26</time>
                </span>
                <span id="/6408.html" class="post-meta-item leancloud_visitors" data-flag-title="第一次见到这种反爬虫。咋办？盘它。" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>3.2k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>3 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6341.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Python <i class="label-arrow"></i>
                  </a>
                  <a href="/6341.html" class="post-title-link" itemprop="url">大众点评还不会爬？跟着我，我教你。</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <h1 id="大家好，我是四毛，好久没有写东西了，欢迎大家关注我的公众号。"><a href="#大家好，我是四毛，好久没有写东西了，欢迎大家关注我的公众号。" class="headerlink" title="大家好，我是四毛，好久没有写东西了，欢迎大家关注我的公众号。"></a>大家好，我是四毛，好久没有写东西了，欢迎大家关注我的公众号。</h1>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/201802111155445124jhQyer.yasuotu.gif" alt=""> 今天的文章是关于如何使用requests来爬取大众点评的数据。 看完本文，你可以：</p>
                  <blockquote>
                    <p>1、了解大众点评的CSS反爬虫机制 2、破解反爬虫机制 3、使用requests即可正确获取到评论数，平均价格，服务，味道，环境数据，评论文本数据；</p>
                  </blockquote>
                  <p> 如果你想跟我继续交流的话，欢迎加我的个人微信，二维码在最后。 <strong>如果你想获取更多的代码，请关注我的公众号，并发送 “大众点评”即可。</strong> 同时，代码我并<strong>没有做太多的优化，因为没有大量的代理，爬不了太多的内容。</strong> <strong>这里只是跟大家分享一下处理的流程。欢迎来公众号留言探讨。</strong> </p>
                  <h1 id="正文开始。"><a href="#正文开始。" class="headerlink" title="正文开始。"></a><strong>正文开始。</strong></h1>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2016/12/兔子.gif" alt=""></p>
                  <h2 id="1-前言"><a href="#1-前言" class="headerlink" title="1.前言"></a>1.前言</h2>
                  <p>在工作生活中，发现越来越多的人对大众点评的数据感兴趣，而大众点评的反爬又是比较严格的。采取的策略差不多是宁可错杀一万，也不放过一个。有的时候正常浏览都会跳出验证码。 另外，在PC端的展示数据是通过CSS来控制的，从网页上看不出来太大的区别，但是用普通的脚本取获取时，会发现数据是获取不到的，具体的源代码是下面这样的： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/微信截图_20181205192407.png" alt=""> 然，在搜资料的时候，你会发现，很多教程都是用的selenium之类的方法，效率太低，没有啥技术含量。 所以，这篇文章的面向的对象就是PC端的大众点评；目标是解决这种反爬虫措施，使用requests获取到干净正确的数据； 跟着我，绝不会让你失望。 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/HGXSM1O5KGB_QWQDIC.gif" alt=""></p>
                  <h2 id="2-正文开始"><a href="#2-正文开始" class="headerlink" title="2.正文开始"></a>2.正文开始</h2>
                  <p>相信搞过大众点评网站的同学都应该知道上面的这种是一个css反爬的方法，具体的解析操作，即将开始。</p>
                  <h4 id="找到藏着秘密的css"><a href="#找到藏着秘密的css" class="headerlink" title="找到藏着秘密的css"></a>找到藏着秘密的css</h4>
                  <p>当我们的鼠标在上面框框内的span上面点击时，会发现右边部分会相应的发生变化： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/微信截图_20181205193028.png" alt=""> 这张图片<strong>很重要，很重要，很重要</strong>，我们要的值，几乎都从这里匹配出来。 这里我们看到了“<strong>vxt20</strong>”这个变量对应的两个像素值，<strong>前面的是控制用哪个数字，后面的是控制用哪一段的数字集合</strong>，先记下，后面要用，同时这里的值应该是6； 这里其实就是整个破解流程最关键的一步了。在这里我们看到了一个链接。 瞎猫当死耗子吧，点进去看看。 <a href="https://s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/f556c0559161832a4c6192e097db3dc2.svg" target="_blank" rel="noopener">https://s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/f556c0559161832a4c6192e097db3dc2.svg</a> 你会发现，返回的是一些数字，我一开始是不知道这是啥玩意的，看过一些大神的解析才知道，其实这里就是我们看到的数字的来源，也就是整个破解的源头，知道了这些数字的含义，也就可以直接破解了整个反爬的流程。 现在直接看源代码： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/微信截图_20181205193754.png" alt=""> 可以看到这里面的几个关键数字：font-size：字体大小；还有几个y的值，我到后面才知道原来这个y是个阈值，起的是个控制的作用。 所以，这一反爬的原理就是：</p>
                  <blockquote>
                    <p>获取属性值与偏移量和阈值映射，然后从svg文件中找到真数据。</p>
                  </blockquote>
                  <p>现在我们就要用到上面的像素值了。</p>
                  <blockquote>
                    <p>1.把所有的值取绝对值； 2.用后面的值来选择用哪个段的数字，这里的值是103，所以使用第三个段的数字集合； 3.因为每个字体是12个像素，所以用163/12=13.58，约等于14，那么我们数一下第14个数字是啥，没错，是6，和预期一样。你可以多试验几次。</p>
                  </blockquote>
                  <p>以上，就是整个破解的逻辑过程。 画个流程图，装个逼： <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/跨职能流程图（水平）.png" alt=""></p>
                  <h2 id="3-Show-Code"><a href="#3-Show-Code" class="headerlink" title="3.Show Code"></a>3.Show Code</h2>
                  <p>下面开始晒代码，俗话说得好，天下代码一大抄。 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2017/02/QQ图片20170205084843.jpg" alt="">这里对主要的步骤代码进行解释， <strong>如果你想获取更多的代码，请关注我的公众号，并发送 “大众点评”即可。</strong>。 <strong>1.获取css_url及span对应的TAG值；</strong></p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_tag</span><span class="params">(_list, offset=<span class="number">1</span>)</span>:</span></span><br><span class="line">    <span class="comment"># 从第一个开始查</span></span><br><span class="line">    _new_list = [data[<span class="number">0</span>:offset] <span class="keyword">for</span> data <span class="keyword">in</span> _list]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> len(set(_new_list)) == <span class="number">1</span>:</span><br><span class="line">        <span class="comment"># 如果set后只有一个值，说明全部重复，这个时候就把offset加1</span></span><br><span class="line">        offset += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> get_tag(_list, offset)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        _return_data = [data[<span class="number">0</span>:offset - <span class="number">1</span>] <span class="keyword">for</span> data <span class="keyword">in</span> _list][<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> _return_data</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_css_and_tag</span><span class="params">(content)</span>:</span></span><br><span class="line">    <span class="string">"""</span></span><br><span class="line"><span class="string">    :param url: 待爬链接</span></span><br><span class="line"><span class="string">    :return: css链接，该span对应的tag</span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    find_css_url = re.search(<span class="string">r'href="([^"]+svgtextcss[^"]+)"'</span>, content, re.M)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> find_css_url:</span><br><span class="line">        <span class="keyword">raise</span> Exception(<span class="string">"cannot find css_url ,check"</span>)</span><br><span class="line">    css_url = find_css_url.group(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    css_url = <span class="string">"https:"</span> + css_url</span><br><span class="line">    <span class="comment"># 这个网页上不同的字段是由不同的css段来进行控制的，所以要找到这个评论数据对应的tag，在这里返回的值为vx；而在获取评论数据时，tag就是fu-;</span></span><br><span class="line">    <span class="comment"># 具体可以观察上面截图的3个span对应的属性值，相等的最长部分为vx</span></span><br><span class="line">    class_tag = re.findall(<span class="string">"&lt;b class=\"(.*?)\"&gt;&lt;/b&gt;"</span>, content)</span><br><span class="line">    _tag = get_tag(class_tag)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> css_url, _tag</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p><strong>2.获取属性与像素值的对应关系</strong></p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_css_and_px_dict</span><span class="params">(css_url)</span>:</span></span><br><span class="line">    con = requests.get(css_url, headers=headers).content.decode(<span class="string">"utf-8"</span>)</span><br><span class="line">    find_datas = re.findall(<span class="string">r'(\.[a-zA-Z0-9-]+)\&#123;background:(\-\d+\.\d+)px (\-\d+\.\d+)px'</span>, con)</span><br><span class="line">    css_name_and_px = &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> data <span class="keyword">in</span> find_datas:</span><br><span class="line">        <span class="comment"># 属性对应的值</span></span><br><span class="line">        span_class_attr_name= data[<span class="number">0</span>][<span class="number">1</span>:]</span><br><span class="line">        <span class="comment"># 偏移量</span></span><br><span class="line">        offset = data[<span class="number">1</span>]</span><br><span class="line">        <span class="comment"># 阈值</span></span><br><span class="line">        position = data[<span class="number">2</span>]</span><br><span class="line">        css_name_and_px[span_class_attr_name] = [offset, position]</span><br><span class="line">    <span class="keyword">return</span> css_name_and_px</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p> <strong>3.获取svg文件的url</strong></p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_svg_threshold_and_int_dict</span><span class="params">(css_url, _tag)</span>:</span></span><br><span class="line">    con = requests.get(css_url, headers=headers).content.decode(<span class="string">"utf-8"</span>)</span><br><span class="line">    index_and_word_dict = &#123;&#125;</span><br><span class="line">    <span class="comment"># 根据tag值匹配到相应的svg的网址</span></span><br><span class="line"></span><br><span class="line">    find_svg_url = re.search(<span class="string">r'span[class\^="%s"].*?background\-image: url\((.*?)\);'</span> % _tag, con)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> find_svg_url:</span><br><span class="line">        <span class="keyword">raise</span> Exception(<span class="string">"cannot find svg file, check"</span>)</span><br><span class="line">    svg_url = find_svg_url.group(<span class="number">1</span>)</span><br><span class="line">    svg_url = <span class="string">"https:"</span> + svg_url</span><br><span class="line">    svg_content = requests.get(svg_url, headers=headers).content</span><br><span class="line">    root = H.document_fromstring(svg_content)</span><br><span class="line">    datas = root.xpath(<span class="string">"//text"</span>)</span><br><span class="line">    <span class="comment"># 把阈值和对应的数字集合放入一个字典中</span></span><br><span class="line">    last = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> index, data <span class="keyword">in</span> enumerate(datas):</span><br><span class="line">        y = int(data.xpath(<span class="string">'@y'</span>)[<span class="number">0</span>])</span><br><span class="line">        int_data = data.xpath(<span class="string">'text()'</span>)[<span class="number">0</span>]</span><br><span class="line">        index_and_word_dict[int_data] = range(last, y+<span class="number">1</span>)</span><br><span class="line">        last = y</span><br><span class="line">    <span class="keyword">return</span> index_and_word_dict</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <p> <strong>4. 获取最终的值</strong></p>
                  <figure class="highlight nix">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line">def get_data(url ):</span><br><span class="line">    <span class="string">""</span><span class="string">"</span></span><br><span class="line"><span class="string">    :param page_url: 待获取url</span></span><br><span class="line"><span class="string">    :return:</span></span><br><span class="line"><span class="string">    "</span><span class="string">""</span></span><br><span class="line">    <span class="attr">con</span> = requests.get(url, <span class="attr">headers=headers).content.decode("utf-8")</span></span><br><span class="line">    <span class="comment"># 获取css url，及tag</span></span><br><span class="line">    css_url, <span class="attr">_tag</span> = get_css(con)</span><br><span class="line">    <span class="comment"># 获取css对应名与像素的映射</span></span><br><span class="line">    <span class="attr">css_and_px_dict</span> = get_css_and_px_dict(css_url)</span><br><span class="line">    <span class="comment"># 获取svg的阈值与数字集合的映射</span></span><br><span class="line">    <span class="attr">svg_threshold_and_int_dict</span> = get_svg_threshold_and_int_dict(css_url, _tag)</span><br><span class="line"></span><br><span class="line">    <span class="attr">doc</span> = etree.HTML(con)</span><br><span class="line">    <span class="attr">shops</span> = doc.xpath('//div[@<span class="attr">id="shop-all-list"]/ul/li')</span></span><br><span class="line">    for shop <span class="keyword">in</span> shops:</span><br><span class="line">        <span class="comment"># 店名</span></span><br><span class="line">        <span class="attr">name</span> = shop.xpath('.//div[@<span class="attr">class="tit"]/a')[0].attrib["title"]</span></span><br><span class="line">        print name</span><br><span class="line">        <span class="attr">comment_num</span> = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">        <span class="attr">comment_and_price_datas</span> = shop.xpath('.//div[@<span class="attr">class="comment"]')</span></span><br><span class="line">        for comment_and_price_data <span class="keyword">in</span> comment_and_price_datas:</span><br><span class="line">            <span class="attr">_comment_data</span> = comment_and_price_data.xpath('a[@<span class="attr">class="review-num"]/b/node()')</span></span><br><span class="line">            <span class="comment"># 遍历每一个node，这里node的类型不同，分别有etree._ElementStringResult(字符)，etree._Element（元素），etree._ElementUnicodeResult（字符）</span></span><br><span class="line">            for _node <span class="keyword">in</span> _comment_data:</span><br><span class="line">                <span class="comment"># 如果是字符，则直接取出</span></span><br><span class="line">                <span class="keyword">if</span> isinstance(_node, etree._ElementStringResult):</span><br><span class="line">                    <span class="attr">comment_num</span> = comment_num * <span class="number">10</span> + int(_node)</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    <span class="comment"># 如果是span类型，则要去找数据</span></span><br><span class="line">                    <span class="comment"># span class的attr</span></span><br><span class="line">                    <span class="attr">span_class_attr_name</span> = _node.attrib[<span class="string">"class"</span>]</span><br><span class="line">                    <span class="comment"># 偏移量，以及所处的段</span></span><br><span class="line">                    offset, <span class="attr">position</span> = css_and_px_dict[span_class_attr_name]</span><br><span class="line">                    <span class="attr">index</span> = abs(int(float(offset) ))</span><br><span class="line">                    <span class="attr">position</span> = abs(int(float(position)))</span><br><span class="line">                    <span class="comment"># 判断</span></span><br><span class="line">                    for key, value <span class="keyword">in</span> svg_threshold_and_int_dict.iteritems():</span><br><span class="line">                        <span class="keyword">if</span> position <span class="keyword">in</span> value:</span><br><span class="line">                            <span class="attr">threshold</span> = int(math.ceil(index/<span class="number">12</span>))</span><br><span class="line">                            <span class="attr">number</span> = int(key[threshold])</span><br><span class="line">                            <span class="attr">comment_num</span> = comment_num * <span class="number">10</span> + number</span><br><span class="line">            print comment_num</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  <h2 id="4-结果展示"><a href="#4-结果展示" class="headerlink" title="4.结果展示"></a>4.结果展示</h2>
                  <h3 id="评论条数数据"><a href="#评论条数数据" class="headerlink" title="评论条数数据"></a>评论条数数据</h3>
                  <p>其实，其他的我都写好了，就不贴了 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/微信截图_20181205205030.png" alt=""> <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/微信截图_20181205205218.png" alt=""> </p>
                  <h3 id="评论具体数据"><a href="#评论具体数据" class="headerlink" title="评论具体数据"></a>评论具体数据</h3>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/微信截图_20181205211804.png" alt=""> <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/微信截图_20181205211954.png" alt=""> </p>
                  <h2 id="5-结语"><a href="#5-结语" class="headerlink" title="5.结语"></a>5.结语</h2>
                  <p>以上就是大众点评Css反爬破解的全部步骤和部分代码。 <strong>如果你想获取更多的代码，请关注我的公众号，并发送 “大众点评”即可。</strong> <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/02/201802111155445124jhQyer.yasuotu.gif" alt=""></p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/四毛" class="author" itemprop="url" rel="index">四毛</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-01-24 14:07:53" itemprop="dateCreated datePublished" datetime="2019-01-24T14:07:53+08:00">2019-01-24</time>
                </span>
                <span id="/6341.html" class="post-meta-item leancloud_visitors" data-flag-title="大众点评还不会爬？跟着我，我教你。" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>4.8k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>4 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6401.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> 个人日记 <i class="label-arrow"></i>
                  </a>
                  <a href="/6401.html" class="post-title-link" itemprop="url">2018 年终总结：成就不算少，进步不算多</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p>最近一段时间没有更新原创文章了，主要是因为最近整个在忙硕士毕业的各种事情，毕业答辩完了以后休假了一小段时间，整个十二月就这么过去了。 转眼已经 2019 年了，其实去年我并没有写年终总结，现在到头来还是蛮后悔的，说实话总结其实还是蛮有必要的，现在就趁着这个时间来对自己的 2018 做一下总结，并立一下 2019 的一些 Flag，再等到 2020 年翻出来打脸吧。 世界上最了解自己的人当然是自己，一句话总结我的 2018 可以说是： <strong>成就不算少，进步不算多</strong> 的一年。为什么成就不算少呢？因为今年可能发生的事相比我之前几年，自己取得了一些从未有的成就，或者完成了一些比较重要的事情。下面就先总结一下自己取得的一些成就吧： <strong>1. 2018 年中出版了自己人生中第一本书籍作品《Python3网络爬虫开发实战》，半年多时间现在已经累计印刷九次，共计 45000 册，上市初长期处在京东科技新书榜第一位的位置，豆瓣评分 9.2。</strong> <strong>2. 2018 年 1 月 20 日正式开始运营自己的个人公众号“进击的Coder”，发布了两百多篇文章，其中大约一半为原创，粉丝现在是 25000 多人。</strong> <strong>3. 发布了自己开发的一款分布式网络爬虫管理框架 Gerapy，可以完成爬虫项目的配置、部署、管理、监控等功能，目前 GitHub 上 Star 数目将近 1000。</strong> <strong>4. 在 GitHub 上创建和贡献了近 100 个项目，目前个人 Followers 达到 3000 人，收获 Star 数目在 2000 左右。</strong> <strong>5. 开始从事机器学习、深度学习方向的研究和开发，并发布了自己开发的一套深度学习脚手架 ModelZoo，并不断对接各种模型，持续完善中。</strong> <strong>6. 比较顺利地完成秋招，并最终拿到了微软的 Offer，达成理想的结果。</strong> <strong>7. 完成了多次大型公开演讲并结识了许多优秀的业界大佬。</strong> <strong>8. 顺利地完成了毕业论文和毕业答辩，成功取得北航硕士学位。</strong> <strong>9. 凭借自己赚得人生第二桶金，实现收入翻番。</strong> <strong>10. Last but not least，她。</strong> 当然还有一些其他的就不再一一列举了。这些可能有的跟一些大佬比起来可能真的是小巫见大巫，不过对我来说，这些成就我总体还是比较满意的。 在这些成就里，有些是真的改变我人生轨迹的事情，有些可能只是我人生路顺理成章踏过去的事情，有的可能只是昙花一现几年后毫无价值的东西。但拿掉其中一个，不管是看似有用或者没用的，也可能会产生意想不到的影响，他们确实是我真实经历过，并出现在我生命旅途中的一部分。 本来把成就一个个地展开来说，但后来想想没这个必要。昨日取得的成就就让它过去吧，不要念过去，把更多的精力着眼在未来吧。重要的在于，我在这一年里收获了多少，这些收获的东西能够为我未来的成长带来多少价值。以及另外重要的是，对过去的反思，我还有哪些做的不好的地方，我需要在未来做怎样的调整。 所以为什么说进步不算多呢？虽然自己达成了某些目标，但我真的没怎么感觉到自己相比去年年初的自己有特别大的成长，或许一切都是潜移默化的，或许真的把我现在放回到 2018年初，和现在的自己相比，我才会感觉到差别，但现在自己仿佛真的感觉自己本身和之前没有太多不同。但总的来说，我还有非常多需要反思和改进的地方。 下面再好好反思下自己吧，我总觉得自己的这一年，自己的有些方面做得不够好，总体体现在这么几个方面吧： <strong>1. 拖延症</strong>。还是觉得自己的拖延症太严重，执行力还不够好。有些事总是会拖个好几天才完成，没太有恒心。或者有的时候这件事到了要做的时间，却又不想做。之前也会有，但是感觉今年犯的很严重。其实我挺佩服那些持之以恒的人的，自制力和执行力真的很重要。 <strong>2. 记录少</strong>。挺多自己觉得会的东西，由于懒或者忙，没有记下来，其实还是因为懒。到头来发现真正学会的其实仅仅就是自己记录的部分，因为当时没做记录的，又忘得一干二净了。所以我总体感觉，今年输出的文章并不多。 <strong>3. 白忙活</strong>。这一年来，其实回想起来，感觉做的无用功是很多了。许多事情感觉做完了之后，真正的价值并不大，所以这个真的需要我来好好权衡。 4. <strong>虎头蛇尾</strong>。由于自己的很多事情安排的满满当当，所以有的项目可能就容易被搁置了，导致有一些项目做得虎头蛇尾，草草收场，想想还是很可惜的，但有时候就是缺乏那重新捡起来的一步动力，这个必须得改。 <strong>5. 时间规划</strong>。我使用 Todoist 已经好多年了，感谢它帮我完成了大部分的时间规划，但有些时候发现还是有些不够好。我多少还会有一些小事优先，大事拖延的倾向，但总体来说已经把握得还算可以了，还得继续加油。 <strong>6. 缺乏锻炼</strong>。平时事情安排的挺多，有时候也懒得去健身房锻炼，唯一锻炼的时间就是睡前的几分钟了，但是效果不大。可能偶尔一时兴起会去几次健身房，但是还是难以坚持下来。虽然还没感觉到身体有啥不适，但也得加强锻炼了。 所以总体感觉下来，感觉到自己做的不够好的地方还有很多，或许真的要改正对我来说真的挺难，但我也要尽力去做的更好。 这一年中，领悟或贯彻的一些理念也不少，其实太多太多，在这里也不能一一列举了，这里就把现在想到的一些说一下吧： <strong>1. 做一件事前，想想这件事的意义和成本有多大，别把时间浪费在无意义的事情上面。</strong> <strong>2. 记录和总结真的很重要。别以为自己当时记住了，等回过头来，你有时候会发现自己的收获就是你所记录的那一部分，没有记录的你想也想不起来了。</strong> <strong>3. 如果自己没本事，认识很多牛人，没什么太大意义，重要的还是自己要牛逼，多投资自己。自己牛逼了，别人就都会看到你了。</strong> <strong>4. 成年人、社会人的世界，有太多大家都已经默认的规矩，自己要去懂，没几个人会教你。</strong> <strong>5. 钱，不能保证能买到所有东西，但是它的确可以提供便利或者免去不必要的麻烦。它是可以增加幸福感的。</strong> <strong>6. 如果想要赚钱，最好不要选和时间或精力成正比的事情，多考虑复利。</strong> <strong>7. 如果一件东西，花了钱，买到的又不是达到自己预期的，这是双亏。如果买到的是自己所满意或喜欢的的，这就是值的。</strong> <strong>8. 选择比努力很重要，眼界比本事更重要。</strong> <strong>9. 所有的一切，如果有能力，就不要将就，可能一时由于自己的能力还无法改变，但一定不要丢了改变下去的动力。</strong> <strong>10. 如果一件事是自己不喜欢的但是非要做，那就用最短的时间去完成。</strong> <strong>11. 健康第一。</strong> 还有很多，就暂且写这些吧。这是我现阶段所领悟到的一些东西，也是我成长路上的一种认知转变过程吧。也可能再过几年我就不会这么想了。不过现阶段我会秉承这些理念继续前行。 最后就立几个 2019 年的 Flag 吧（等明年翻出来打脸： <strong>1. 踏入社会和职场的第一年，好好工作，把握好节奏。</strong> <strong>2. 完成《Python3网络爬虫开发实战（第二版）》以及现在初步规划的另外的两部作品（名称尚未完全确定暂不透露。</strong> <strong>3. 微信公众号读者粉丝在 2019 年底达到 10 万。</strong> <strong>4. 自己的 Gerapy、ModelZoo 两个项目持续运营维护，力求完善。</strong> <strong>5. 坚持记录，持续输出（内容规划了挺多），达到自己内心中设定的基本目标。</strong> <strong>6. 2019 年年收入比 2018 年继续翻番。</strong> <strong>7. 结识更多的人，扩展更广的人脉。</strong> <strong>8. Last but not least，和她好好地在一起，过我们的向往的生活，做喜欢的事情。</strong> 好啦，感谢大家一直以来的支持！新的一年，继续努力！愿我们都成为更好的自己！</p>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/崔庆才" class="author" itemprop="url" rel="index">崔庆才</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2019-01-06 00:31:48" itemprop="dateCreated datePublished" datetime="2019-01-06T00:31:48+08:00">2019-01-06</time>
                </span>
                <span id="/6401.html" class="post-meta-item leancloud_visitors" data-flag-title="2018 年终总结：成就不算少，进步不算多" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>2.9k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>3 分钟</span>
                </span>
              </div>
            </article>
            <article itemscope itemtype="http://schema.org/Article" class="post-block index" lang="zh-CN">
              <link itemprop="mainEntityOfPage" href="https://cuiqingcai.com/6378.html">
              <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
                <meta itemprop="image" content="/images/avatar.png">
                <meta itemprop="name" content="崔庆才">
                <meta itemprop="description" content="崔庆才的个人站点，记录生活的瞬间，分享学习的心得。">
              </span>
              <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <meta itemprop="name" content="静觅">
              </span>
              <header class="post-header">
                <h2 class="post-title" itemprop="name headline">
                  <a class="label"> Linux <i class="label-arrow"></i>
                  </a>
                  <a href="/6378.html" class="post-title-link" itemprop="url">小白学爬虫之代理(公有云)</a>
                </h2>
              </header>
              <div class="post-body" itemprop="articleBody">
                <div class="thumb">
                  <img itemprop="contentUrl" class="random">
                </div>
                <div class="excerpt">
                  <p>
                  <p><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/HGXSM1O5KGB_QWQDIC.gif" alt=""> Hello 各位小伙伴 雷门吼！</p>
                  <h2 id="在教程之前首先申明！此教程适合土豪不缺钱的玩家"><a href="#在教程之前首先申明！此教程适合土豪不缺钱的玩家" class="headerlink" title="在教程之前首先申明！此教程适合土豪不缺钱的玩家"></a><strong>在教程之前首先申明！此教程适合土豪不缺钱的玩家</strong></h2>
                  <p>潜水了许久了，今天来更新点东西~ 今天说点啥呢？ 那就是<strong>代理</strong>！！ 代理在爬虫界的重要作用相信各位应该清楚吧！毕竟绝大部分反爬可以靠<strong>代理</strong>解决；不能被代理解决的也得要<strong>代理</strong>配合解决。 <img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/03/FCADE87DD50A4DE0DCB39A3FA5F4A5C0-1.jpg" alt=""> 市面上各种代理也是琳琅满目的说··· 相信大家最喜欢用的之一应该就是 <strong>某布云。</strong> 根据官网的显示他他家的代理是这个样子的：</p>
                  <ul>
                    <li><strong>无须切换 IP，每一个请求一个随机 IP。</strong></li>
                  </ul>
                  <p>哇！感觉很爽的样子今天我们就来实现一个类似的代理！ 其实 So Easy！ 我们需要借助 <strong>公有云</strong> 来实现。 下面我以 AWS 举例(其它公有云操作类似，唯一的区别的就是：各个服务的名字不同而已) </p>
                  <ol>
                    <li>首先我们需要需要使用EC2来建立一个代理（Google Could 叫 GCE）<ol>
                        <li>安装Squid（当然你可以使用其它的代理）<ol>
                            <li>无认证安装参考这儿(<a href="https://cuiqingcai.com/3443.html">点我</a>)设置代理服务器那一段</li>
                            <li>MySQL认证安装(<a href="https://www.thsheep.com/post/squid+mysql%E8%AE%A4%E8%AF%81/" target="_blank" rel="noopener">点我</a>)</li>
                            <li><strong>Note: 请注意检查！！！务必设置Ipv4转发</strong></li>
                          </ol>
                        </li>
                      </ol>
                    </li>
                    <li>安装完成之后我们制作启动模板（毕竟一个EC2 一个IP 你总不能安装很多很多台吧！会死人的）<ol>
                        <li>注意设置你的安全组！正常情况下 入站规则只应该有你需要的端口（squid使用的端口一定要放心！嫌麻烦的小伙伴儿 可以进出都放行全部流量！）出站则是全部流量！</li>
                        <li>启动EC2的时候选择安全组<strong>一定要看清是否是设置过放行的规则</strong>！ 不要选错了！</li>
                        <li><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/ec2.png" alt=""></li>
                        <li><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/制作启动镜像01.jpg" alt=""></li>
                        <li><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/制作启动镜像02.png" alt=""></li>
                        <li>好了现在就可以批量启动了！<img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/从模板启动实例.png" alt=""></li>
                        <li>实例数量就是需要<strong>同时</strong>有多少个IP就启动多少个了。<img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/从模板启动实例01.png" alt=""></li>
                        <li><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/启动结果.png" alt=""></li>
                      </ol>
                    </li>
                    <li>设置前端负载均衡（提供一个固定地址，这个地址负责随机将请求转发到后端代理服务器上）<ol>
                        <li><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/负载均衡器01.png" alt=""></li>
                        <li>必须使用TCP四层负载！原因为啥大家自己百度一哈<img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/负载均衡器02.png" alt=""></li>
                        <li><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/负载均衡器03.png" alt=""></li>
                        <li><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/负载均衡器04.png" alt=""></li>
                        <li><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/负载均衡器05.png" alt=""></li>
                        <li>等待负载均衡器启动完成！</li>
                        <li>启动完成后获取负载地址<img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/代理地址.png" alt=""></li>
                      </ol>
                    </li>
                    <li>下面来测试一下效果！<ol>
                        <li><img src="https://qiniu.cuiqingcai.com/wp-content/uploads/2018/12/测试IP.png" alt=""></li>
                      </ol>
                    </li>
                  </ol>
                  <p>以上完毕！你可以不停的重启Ec2实例！你就有百万IP池啦！！（前提是你有钱啊） 下面是重启Ec2的示例：</p>
                  <figure class="highlight python">
                    <table>
                      <tr>
                        <td class="gutter">
                          <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre>
                        </td>
                        <td class="code">
                          <pre><span class="line"><span class="keyword">import</span> boto3</span><br><span class="line"><span class="keyword">from</span> boto3 <span class="keyword">import</span> setup_default_session</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">setup_default_session(aws_access_key_id=<span class="string">'XXXXXX'</span>,</span><br><span class="line">                      aws_secret_access_key=<span class="string">'XXXXXX'</span>,</span><br><span class="line">                      region_name=<span class="string">'区域'</span>)</span><br><span class="line"></span><br><span class="line">ec2 = boto3.client(<span class="string">'ec2'</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_public_ip_address</span><span class="params">()</span>:</span></span><br><span class="line">    <span class="string">"""</span></span><br><span class="line"><span class="string">    获取IP和实例ID</span></span><br><span class="line"><span class="string">    :return: &#123;实例ID: ip&#125;</span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    response = ec2.describe_instances()</span><br><span class="line">    reservations = response.get(<span class="string">'Reservations'</span>)</span><br><span class="line">    instances = [i.get(<span class="string">'Instances'</span>)[<span class="number">0</span>] <span class="keyword">for</span> i <span class="keyword">in</span> reservations]</span><br><span class="line">    instance_id_public_ip_address = &#123;i.get(<span class="string">'InstanceId'</span>): i.get(<span class="string">'PublicIpAddress'</span>) <span class="keyword">for</span> i <span class="keyword">in</span> instances&#125;</span><br><span class="line">    <span class="keyword">return</span> instance_id_public_ip_address</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">reboot_ec2</span><span class="params">(ip)</span>:</span></span><br><span class="line">    <span class="string">"""重启实例</span></span><br><span class="line"><span class="string">    :param ip: </span></span><br><span class="line"><span class="string">    :return: </span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    instance_id_public_ip_address = get_public_ip_address()</span><br><span class="line">    instance_id = instance_id_public_ip_address.get(ip)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        ec2.reboot_instances(InstanceIds=[instance_id], DryRun=<span class="literal">True</span>)</span><br><span class="line">    <span class="keyword">except</span> ClientError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">if</span> <span class="string">'DryRunOperation'</span> <span class="keyword">not</span> <span class="keyword">in</span> str(e):</span><br><span class="line">            print(<span class="string">"You don't have permission to reboot instances."</span>)</span><br><span class="line">            <span class="keyword">raise</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        response = ec2.reboot_instances(InstanceIds=[instance_id], DryRun=<span class="literal">False</span>)</span><br><span class="line">        print(<span class="string">'Success'</span>, response)</span><br><span class="line">    <span class="keyword">except</span> ClientError <span class="keyword">as</span> e:</span><br><span class="line">        print(<span class="string">'Error'</span>, e)</span><br></pre>
                        </td>
                      </tr>
                    </table>
                  </figure>
                  </p>
                </div>
              </div>
              <div class="post-meta">
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-user"></i>
                  </span>
                  <span class="post-meta-item-text">作者</span>
                  <span><a href="/authors/哎哟卧槽" class="author" itemprop="url" rel="index">哎哟卧槽</a></span>
                </span>
                <span class="post-meta-item">
                  <span class="post-meta-item-icon">
                    <i class="far fa-calendar"></i>
                  </span>
                  <span class="post-meta-item-text">发表于</span>
                  <time title="创建时间：2018-12-26 15:59:15" itemprop="dateCreated datePublished" datetime="2018-12-26T15:59:15+08:00">2018-12-26</time>
                </span>
                <span id="/6378.html" class="post-meta-item leancloud_visitors" data-flag-title="小白学爬虫之代理(公有云)" title="阅读次数">
                  <span class="post-meta-item-icon">
                    <i class="fa fa-eye"></i>
                  </span>
                  <span class="post-meta-item-text">阅读次数：</span>
                  <span class="leancloud-visitors-count"></span>
                </span>
                <span class="post-meta-item" title="本文字数">
                  <span class="post-meta-item-icon">
                    <i class="far fa-file-word"></i>
                  </span>
                  <span class="post-meta-item-text">本文字数：</span>
                  <span>1.8k</span>
                </span>
                <span class="post-meta-item" title="阅读时长">
                  <span class="post-meta-item-icon">
                    <i class="far fa-clock"></i>
                  </span>
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                  <span>2 分钟</span>
                </span>
              </div>
            </article>
            <script>
              document.querySelectorAll('.random').forEach(item => item.src = "https://picsum.photos/id/" + Math.floor(Math.random() * Math.floor(300)) + "/200/133")

            </script>
            <nav class="pagination">
              <a class="extend prev" rel="prev" href="/page/13/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/13/">13</a><span class="page-number current">14</span><a class="page-number" href="/page/15/">15</a><span class="space">&hellip;</span><a class="page-number" href="/page/31/">31</a><a class="extend next" rel="next" href="/page/15/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
            </nav>
          </div>
          <script>
            window.addEventListener('tabs:register', () =>
            {
              let
              {
                activeClass
              } = CONFIG.comments;
              if (CONFIG.comments.storage)
              {
                activeClass = localStorage.getItem('comments_active') || activeClass;
              }
              if (activeClass)
              {
                let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
                if (activeTab)
                {
                  activeTab.click();
                }
              }
            });
            if (CONFIG.comments.storage)
            {
              window.addEventListener('tabs:click', event =>
              {
                if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
                let commentClass = event.target.classList[1];
                localStorage.setItem('comments_active', commentClass);
              });
            }

          </script>
        </div>
        <div class="toggle sidebar-toggle">
          <span class="toggle-line toggle-line-first"></span>
          <span class="toggle-line toggle-line-middle"></span>
          <span class="toggle-line toggle-line-last"></span>
        </div>
        <aside class="sidebar">
          <div class="sidebar-inner">
            <ul class="sidebar-nav motion-element">
              <li class="sidebar-nav-toc"> 文章目录 </li>
              <li class="sidebar-nav-overview"> 站点概览 </li>
            </ul>
            <!--noindex-->
            <div class="post-toc-wrap sidebar-panel">
            </div>
            <!--/noindex-->
            <div class="site-overview-wrap sidebar-panel">
              <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
                <img class="site-author-image" itemprop="image" alt="崔庆才" src="/images/avatar.png">
                <p class="site-author-name" itemprop="name">崔庆才</p>
                <div class="site-description" itemprop="description">崔庆才的个人站点，记录生活的瞬间，分享学习的心得。</div>
              </div>
              <div class="site-state-wrap motion-element">
                <nav class="site-state">
                  <div class="site-state-item site-state-posts">
                    <a href="/archives/">
                      <span class="site-state-item-count">608</span>
                      <span class="site-state-item-name">日志</span>
                    </a>
                  </div>
                  <div class="site-state-item site-state-categories">
                    <a href="/categories/">
                      <span class="site-state-item-count">24</span>
                      <span class="site-state-item-name">分类</span></a>
                  </div>
                  <div class="site-state-item site-state-tags">
                    <a href="/tags/">
                      <span class="site-state-item-count">156</span>
                      <span class="site-state-item-name">标签</span></a>
                  </div>
                </nav>
              </div>
              <div class="links-of-author motion-element">
                <span class="links-of-author-item">
                  <a href="https://github.com/Germey" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;Germey" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
                </span>
                <span class="links-of-author-item">
                  <a href="mailto:cqc@cuiqingcai.com.com" title="邮件 → mailto:cqc@cuiqingcai.com.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>邮件</a>
                </span>
                <span class="links-of-author-item">
                  <a href="https://weibo.com/cuiqingcai" title="微博 → https:&#x2F;&#x2F;weibo.com&#x2F;cuiqingcai" rel="noopener" target="_blank"><i class="fab fa-weibo fa-fw"></i>微博</a>
                </span>
                <span class="links-of-author-item">
                  <a href="https://www.zhihu.com/people/Germey" title="知乎 → https:&#x2F;&#x2F;www.zhihu.com&#x2F;people&#x2F;Germey" rel="noopener" target="_blank"><i class="fa fa-magic fa-fw"></i>知乎</a>
                </span>
              </div>
            </div>
            <div style=" width: 100%;" class="sidebar-panel sidebar-panel-image sidebar-panel-active">
              <a href="https://tutorial.lengyue.video/?coupon=12ef4b1a-a3db-11ea-bb37-0242ac130002_cqx_850" target="_blank" rel="noopener">
                <img src="https://qiniu.cuiqingcai.com/bco2a.png" style=" width: 100%;">
              </a>
            </div>
            <div style=" width: 100%;" class="sidebar-panel sidebar-panel-image sidebar-panel-active">
              <a href="http://www.ipidea.net/?utm-source=cqc&utm-keyword=?cqc" target="_blank" rel="noopener">
                <img src="https://qiniu.cuiqingcai.com/0ywun.png" style=" width: 100%;">
              </a>
            </div>
            <div class="sidebar-panel sidebar-panel-tags sidebar-panel-active">
              <h4 class="name"> 标签云 </h4>
              <div class="content">
                <a href="/tags/2048/" style="font-size: 10px;">2048</a> <a href="/tags/API/" style="font-size: 10px;">API</a> <a href="/tags/Bootstrap/" style="font-size: 11.25px;">Bootstrap</a> <a href="/tags/CDN/" style="font-size: 10px;">CDN</a> <a href="/tags/CQC/" style="font-size: 10px;">CQC</a> <a href="/tags/CSS/" style="font-size: 10px;">CSS</a> <a href="/tags/CSS-%E5%8F%8D%E7%88%AC%E8%99%AB/" style="font-size: 10px;">CSS 反爬虫</a> <a href="/tags/CV/" style="font-size: 10px;">CV</a> <a href="/tags/Django/" style="font-size: 10px;">Django</a> <a href="/tags/Eclipse/" style="font-size: 11.25px;">Eclipse</a> <a href="/tags/FTP/" style="font-size: 10px;">FTP</a> <a href="/tags/Git/" style="font-size: 10px;">Git</a> <a href="/tags/GitHub/" style="font-size: 13.75px;">GitHub</a> <a href="/tags/HTML5/" style="font-size: 10px;">HTML5</a> <a href="/tags/Hexo/" style="font-size: 10px;">Hexo</a> <a href="/tags/IT/" style="font-size: 10px;">IT</a> <a href="/tags/JSP/" style="font-size: 10px;">JSP</a> <a href="/tags/JavaScript/" style="font-size: 10px;">JavaScript</a> <a href="/tags/K8s/" style="font-size: 10px;">K8s</a> <a href="/tags/LOGO/" style="font-size: 10px;">LOGO</a> <a href="/tags/Linux/" style="font-size: 10px;">Linux</a> <a href="/tags/MIUI/" style="font-size: 10px;">MIUI</a> <a href="/tags/MongoDB/" style="font-size: 10px;">MongoDB</a> <a href="/tags/Mysql/" style="font-size: 10px;">Mysql</a> <a href="/tags/NBA/" style="font-size: 10px;">NBA</a> <a href="/tags/PHP/" style="font-size: 11.25px;">PHP</a> <a href="/tags/PS/" style="font-size: 10px;">PS</a> <a href="/tags/Pathlib/" style="font-size: 10px;">Pathlib</a> <a href="/tags/PhantomJS/" style="font-size: 10px;">PhantomJS</a> <a href="/tags/Python/" style="font-size: 15px;">Python</a> <a href="/tags/Python3/" style="font-size: 12.5px;">Python3</a> <a href="/tags/Pythonic/" style="font-size: 10px;">Pythonic</a> <a href="/tags/QQ/" style="font-size: 10px;">QQ</a> <a href="/tags/Redis/" style="font-size: 10px;">Redis</a> <a href="/tags/SAE/" style="font-size: 10px;">SAE</a> <a href="/tags/SSH/" style="font-size: 10px;">SSH</a> <a href="/tags/SVG/" style="font-size: 10px;">SVG</a> <a href="/tags/Scrapy/" style="font-size: 10px;">Scrapy</a> <a href="/tags/Scrapy-redis/" style="font-size: 10px;">Scrapy-redis</a> <a href="/tags/Scrapy%E5%88%86%E5%B8%83%E5%BC%8F/" style="font-size: 10px;">Scrapy分布式</a> <a href="/tags/Selenium/" style="font-size: 10px;">Selenium</a> <a href="/tags/TKE/" style="font-size: 10px;">TKE</a> <a href="/tags/Ubuntu/" style="font-size: 11.25px;">Ubuntu</a> <a href="/tags/VS-Code/" style="font-size: 10px;">VS Code</a> <a href="/tags/Vs-Code/" style="font-size: 10px;">Vs Code</a> <a href="/tags/Vue/" style="font-size: 11.25px;">Vue</a> <a href="/tags/Webpack/" style="font-size: 10px;">Webpack</a> <a href="/tags/Windows/" style="font-size: 10px;">Windows</a> <a href="/tags/Winpcap/" style="font-size: 10px;">Winpcap</a> <a href="/tags/WordPress/" style="font-size: 13.75px;">WordPress</a> <a href="/tags/Youtube/" style="font-size: 11.25px;">Youtube</a> <a href="/tags/android/" style="font-size: 10px;">android</a> <a href="/tags/ansible/" style="font-size: 10px;">ansible</a> <a href="/tags/cocos2d-x/" style="font-size: 10px;">cocos2d-x</a> <a href="/tags/e6/" style="font-size: 10px;">e6</a> <a href="/tags/fitvids/" style="font-size: 10px;">fitvids</a> <a href="/tags/git/" style="font-size: 11.25px;">git</a> <a href="/tags/json/" style="font-size: 10px;">json</a> <a href="/tags/js%E9%80%86%E5%90%91/" style="font-size: 10px;">js逆向</a> <a href="/tags/kubernetes/" style="font-size: 10px;">kubernetes</a> <a href="/tags/log/" style="font-size: 10px;">log</a> <a href="/tags/logging/" style="font-size: 10px;">logging</a> <a href="/tags/matlab/" style="font-size: 11.25px;">matlab</a> <a href="/tags/python/" style="font-size: 20px;">python</a> <a href="/tags/pytube/" style="font-size: 11.25px;">pytube</a> <a href="/tags/pywin32/" style="font-size: 10px;">pywin32</a> <a href="/tags/style/" style="font-size: 10px;">style</a> <a href="/tags/tomcat/" style="font-size: 10px;">tomcat</a> <a href="/tags/ubuntu/" style="font-size: 10px;">ubuntu</a> <a href="/tags/uwsgi/" style="font-size: 10px;">uwsgi</a> <a href="/tags/vsftpd/" style="font-size: 10px;">vsftpd</a> <a href="/tags/wamp/" style="font-size: 10px;">wamp</a> <a href="/tags/wineQQ/" style="font-size: 10px;">wineQQ</a> <a href="/tags/%E4%B8%83%E7%89%9B/" style="font-size: 11.25px;">七牛</a> <a href="/tags/%E4%B8%8A%E6%B5%B7/" style="font-size: 10px;">上海</a> <a href="/tags/%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99/" style="font-size: 10px;">个人网站</a> <a href="/tags/%E4%B8%BB%E9%A2%98/" style="font-size: 10px;">主题</a> <a href="/tags/%E4%BA%91%E4%BA%A7%E5%93%81/" style="font-size: 10px;">云产品</a> <a href="/tags/%E4%BA%91%E5%AD%98%E5%82%A8/" style="font-size: 10px;">云存储</a> <a href="/tags/%E4%BA%AC%E4%B8%9C%E4%BA%91/" style="font-size: 10px;">京东云</a> <a href="/tags/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/" style="font-size: 12.5px;">人工智能</a> <a href="/tags/%E4%BB%A3%E7%90%86/" style="font-size: 10px;">代理</a> <a href="/tags/%E4%BB%A3%E7%A0%81/" style="font-size: 10px;">代码</a> <a href="/tags/%E4%BB%A3%E7%A0%81%E5%88%86%E4%BA%AB%E5%9B%BE/" style="font-size: 10px;">代码分享图</a> <a href="/tags/%E4%BC%98%E5%8C%96/" style="font-size: 10px;">优化</a> <a href="/tags/%E4%BD%8D%E8%BF%90%E7%AE%97/" style="font-size: 10px;">位运算</a> <a href="/tags/%E5%85%AC%E4%BC%97%E5%8F%B7/" style="font-size: 10px;">公众号</a> <a href="/tags/%E5%88%86%E4%BA%AB/" style="font-size: 10px;">分享</a> <a href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F/" style="font-size: 10px;">分布式</a> <a href="/tags/%E5%88%9B%E4%B8%9A/" style="font-size: 10px;">创业</a> <a href="/tags/%E5%89%8D%E7%AB%AF/" style="font-size: 12.5px;">前端</a> <a href="/tags/%E5%8D%9A%E5%AE%A2/" style="font-size: 10px;">博客</a> <a href="/tags/%E5%8E%9F%E7%94%9FAPP/" style="font-size: 10px;">原生APP</a> <a href="/tags/%E5%8F%8D%E7%88%AC%E8%99%AB/" style="font-size: 12.5px;">反爬虫</a> <a href="/tags/%E5%91%BD%E4%BB%A4/" style="font-size: 10px;">命令</a> <a href="/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E5%B8%83%E5%B1%80/" style="font-size: 10px;">响应式布局</a> <a href="/tags/%E5%9E%83%E5%9C%BE%E9%82%AE%E4%BB%B6/" style="font-size: 10px;">垃圾邮件</a> <a href="/tags/%E5%9F%9F%E5%90%8D%E7%BB%91%E5%AE%9A/" style="font-size: 10px;">域名绑定</a> <a href="/tags/%E5%A4%8D%E7%9B%98/" style="font-size: 10px;">复盘</a> <a href="/tags/%E5%A4%A7%E4%BC%97%E7%82%B9%E8%AF%84/" style="font-size: 10px;">大众点评</a> <a href="/tags/%E5%AD%97%E4%BD%93%E5%8F%8D%E7%88%AC%E8%99%AB/" style="font-size: 10px;">字体反爬虫</a> <a href="/tags/%E5%AD%97%E7%AC%A6%E9%97%AE%E9%A2%98/" style="font-size: 10px;">字符问题</a> <a href="/tags/%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95/" style="font-size: 10px;">学习方法</a> <a href="/tags/%E5%AE%89%E5%8D%93/" style="font-size: 10px;">安卓</a> <a href="/tags/%E5%AE%9E%E7%94%A8/" style="font-size: 10px;">实用</a> <a href="/tags/%E5%B0%81%E9%9D%A2/" style="font-size: 10px;">封面</a> <a href="/tags/%E5%B4%94%E5%BA%86%E6%89%8D/" style="font-size: 18.75px;">崔庆才</a> <a href="/tags/%E5%B7%A5%E5%85%B7/" style="font-size: 12.5px;">工具</a> <a href="/tags/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/" style="font-size: 10px;">开发工具</a> <a href="/tags/%E5%BE%AE%E8%BD%AF/" style="font-size: 10px;">微软</a> <a href="/tags/%E6%80%9D%E8%80%83/" style="font-size: 10px;">思考</a> <a href="/tags/%E6%89%8B%E6%9C%BA%E8%AE%BF%E9%97%AE/" style="font-size: 10px;">手机访问</a> <a href="/tags/%E6%95%99%E7%A8%8B/" style="font-size: 10px;">教程</a> <a href="/tags/%E6%95%99%E8%82%B2/" style="font-size: 10px;">教育</a> <a href="/tags/%E6%96%B0%E4%B9%A6/" style="font-size: 12.5px;">新书</a> <a href="/tags/%E6%96%B9%E6%B3%95%E8%AE%BA/" style="font-size: 10px;">方法论</a> <a href="/tags/%E6%97%85%E6%B8%B8/" style="font-size: 10px;">旅游</a> <a href="/tags/%E6%97%A5%E5%BF%97/" style="font-size: 10px;">日志</a> <a href="/tags/%E6%9A%97%E6%97%B6%E9%97%B4/" style="font-size: 10px;">暗时间</a> <a href="/tags/%E6%9D%9C%E5%85%B0%E7%89%B9/" style="font-size: 11.25px;">杜兰特</a> <a href="/tags/%E6%A1%8C%E9%9D%A2/" style="font-size: 10px;">桌面</a> <a href="/tags/%E6%AD%8C%E5%8D%95/" style="font-size: 10px;">歌单</a> <a href="/tags/%E6%B1%9F%E5%8D%97/" style="font-size: 10px;">江南</a> <a href="/tags/%E6%B8%B8%E6%88%8F/" style="font-size: 10px;">游戏</a> <a href="/tags/%E7%84%A6%E8%99%91/" style="font-size: 10px;">焦虑</a> <a href="/tags/%E7%88%AC%E8%99%AB/" style="font-size: 16.25px;">爬虫</a> <a href="/tags/%E7%88%AC%E8%99%AB%E4%B9%A6%E7%B1%8D/" style="font-size: 11.25px;">爬虫书籍</a> <a href="/tags/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F/" style="font-size: 10px;">环境变量</a> <a href="/tags/%E7%94%9F%E6%B4%BB%E7%AC%94%E8%AE%B0/" style="font-size: 10px;">生活笔记</a> <a href="/tags/%E7%99%BB%E5%BD%95/" style="font-size: 10px;">登录</a> <a href="/tags/%E7%9F%A5%E4%B9%8E/" style="font-size: 10px;">知乎</a> <a href="/tags/%E7%9F%AD%E4%BF%A1/" style="font-size: 10px;">短信</a> <a href="/tags/%E7%9F%AD%E4%BF%A1%E9%AA%8C%E8%AF%81%E7%A0%81/" style="font-size: 10px;">短信验证码</a> <a href="/tags/%E7%AC%94%E8%AE%B0%E8%BD%AF%E4%BB%B6/" style="font-size: 10px;">笔记软件</a> <a href="/tags/%E7%AF%AE%E7%BD%91/" style="font-size: 10px;">篮网</a> <a href="/tags/%E7%BA%B8%E5%BC%A0/" style="font-size: 10px;">纸张</a> <a href="/tags/%E7%BB%84%E4%BB%B6/" style="font-size: 10px;">组件</a> <a href="/tags/%E7%BD%91%E7%AB%99/" style="font-size: 10px;">网站</a> <a href="/tags/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/" style="font-size: 11.25px;">网络爬虫</a> <a href="/tags/%E7%BE%8E%E5%AD%A6/" style="font-size: 10px;">美学</a> <a href="/tags/%E8%82%89%E5%A4%B9%E9%A6%8D/" style="font-size: 10px;">肉夹馍</a> <a href="/tags/%E8%85%BE%E8%AE%AF%E4%BA%91/" style="font-size: 10px;">腾讯云</a> <a href="/tags/%E8%87%AA%E5%BE%8B/" style="font-size: 10px;">自律</a> <a href="/tags/%E8%A5%BF%E5%B0%91%E7%88%B7/" style="font-size: 10px;">西少爷</a> <a href="/tags/%E8%A7%86%E9%A2%91/" style="font-size: 10px;">视频</a> <a href="/tags/%E8%B0%B7%E6%AD%8C%E9%AA%8C%E8%AF%81%E7%A0%81/" style="font-size: 10px;">谷歌验证码</a> <a href="/tags/%E8%BF%90%E8%90%A5/" style="font-size: 10px;">运营</a> <a href="/tags/%E8%BF%9C%E7%A8%8B/" style="font-size: 10px;">远程</a> <a href="/tags/%E9%80%86%E5%90%91/" style="font-size: 10px;">逆向</a> <a href="/tags/%E9%85%8D%E7%BD%AE/" style="font-size: 10px;">配置</a> <a href="/tags/%E9%87%8D%E8%A3%85/" style="font-size: 10px;">重装</a> <a href="/tags/%E9%98%BF%E6%9D%9C/" style="font-size: 10px;">阿杜</a> <a href="/tags/%E9%9D%99%E8%A7%85/" style="font-size: 17.5px;">静觅</a> <a href="/tags/%E9%A2%A0%E8%A6%86/" style="font-size: 10px;">颠覆</a> <a href="/tags/%E9%A3%9E%E4%BF%A1/" style="font-size: 10px;">飞信</a> <a href="/tags/%E9%B8%BF%E8%92%99/" style="font-size: 10px;">鸿蒙</a>
              </div>
              <script>
                const tagsColors = ['#00a67c', '#5cb85c', '#d9534f', '#567e95', '#b37333', '#f4843d', '#15a287']
                const tagsElements = document.querySelectorAll('.sidebar-panel-tags .content a')
                tagsElements.forEach((item) =>
                {
                  item.style.backgroundColor = tagsColors[Math.floor(Math.random() * tagsColors.length)]
                })

              </script>
            </div>
            <div class="sidebar-panel sidebar-panel-categories sidebar-panel-active">
              <h4 class="name"> 分类 </h4>
              <div class="content">
                <ul class="category-list">
                  <li class="category-list-item"><a class="category-list-link" href="/categories/C-C/">C/C++</a><span class="category-list-count">23</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/HTML/">HTML</a><span class="category-list-count">14</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Java/">Java</a><span class="category-list-count">5</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/JavaScript/">JavaScript</a><span class="category-list-count">26</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Linux/">Linux</a><span class="category-list-count">15</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Markdown/">Markdown</a><span class="category-list-count">1</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Net/">Net</a><span class="category-list-count">4</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Other/">Other</a><span class="category-list-count">39</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/PHP/">PHP</a><span class="category-list-count">27</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Paper/">Paper</a><span class="category-list-count">2</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/Python/">Python</a><span class="category-list-count">261</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/TypeScript/">TypeScript</a><span class="category-list-count">2</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E4%B8%AA%E4%BA%BA%E5%B1%95%E7%A4%BA/">个人展示</a><span class="category-list-count">1</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E4%B8%AA%E4%BA%BA%E6%97%A5%E8%AE%B0/">个人日记</a><span class="category-list-count">9</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E4%B8%AA%E4%BA%BA%E8%AE%B0%E5%BD%95/">个人记录</a><span class="category-list-count">4</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E4%B8%AA%E4%BA%BA%E9%9A%8F%E7%AC%94/">个人随笔</a><span class="category-list-count">15</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE/">安装配置</a><span class="category-list-count">59</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E6%8A%80%E6%9C%AF%E6%9D%82%E8%B0%88/">技术杂谈</a><span class="category-list-count">88</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E6%9C%AA%E5%88%86%E7%B1%BB/">未分类</a><span class="category-list-count">1</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E7%94%9F%E6%B4%BB%E7%AC%94%E8%AE%B0/">生活笔记</a><span class="category-list-count">1</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E7%A6%8F%E5%88%A9%E4%B8%93%E5%8C%BA/">福利专区</a><span class="category-list-count">6</span></li>
                  <li class="category-list-item"><a class="category-list-link" href="/categories/%E8%81%8C%E4%BD%8D%E6%8E%A8%E8%8D%90/">职位推荐</a><span class="category-list-count">2</span></li>
                </ul>
              </div>
            </div>
            <div class="sidebar-panel sidebar-panel-friends sidebar-panel-active">
              <h4 class="name"> 友情链接 </h4>
              <ul class="friends">
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/j2dub.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.findhao.net/" target="_blank" rel="noopener">FindHao</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/ou6mm.jpg">
                  </span>
                  <span class="link">
                    <a href="https://diygod.me/" target="_blank" rel="noopener">DIYgod</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/6apxu.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.51dev.com/" target="_blank" rel="noopener">IT技术社区</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://www.jankl.com/img/titleshu.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.jankl.com/" target="_blank" rel="noopener">liberalist</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/bqlbs.png">
                  </span>
                  <span class="link">
                    <a href="http://www.urselect.com/" target="_blank" rel="noopener">优社电商</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/8s88c.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.yuanrenxue.com/" target="_blank" rel="noopener">猿人学</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/2wgg5.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.yunlifang.cn/" target="_blank" rel="noopener">云立方</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/shwr6.png">
                  </span>
                  <span class="link">
                    <a href="http://lanbing510.info/" target="_blank" rel="noopener">冰蓝</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/blvoh.jpg">
                  </span>
                  <span class="link">
                    <a href="https://lengyue.me/" target="_blank" rel="noopener">冷月</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="http://qianxunclub.com/favicon.png">
                  </span>
                  <span class="link">
                    <a href="http://qianxunclub.com/" target="_blank" rel="noopener">千寻啊千寻</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/0044u.jpg">
                  </span>
                  <span class="link">
                    <a href="http://kodcloud.com/" target="_blank" rel="noopener">可道云</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/ygnpn.jpg">
                  </span>
                  <span class="link">
                    <a href="http://www.kunkundashen.cn/" target="_blank" rel="noopener">坤坤大神</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/22uv1.png">
                  </span>
                  <span class="link">
                    <a href="http://www.cenchong.com/" target="_blank" rel="noopener">岑冲博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/ev9kl.png">
                  </span>
                  <span class="link">
                    <a href="http://www.zxiaoji.com/" target="_blank" rel="noopener">张小鸡</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://www.503error.com/favicon.ico">
                  </span>
                  <span class="link">
                    <a href="https://www.503error.com/" target="_blank" rel="noopener">张志明个人博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/x714o.jpg">
                  </span>
                  <span class="link">
                    <a href="http://www.hubwiz.com/" target="_blank" rel="noopener">汇智网</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/129d8.png">
                  </span>
                  <span class="link">
                    <a href="https://www.bysocket.com/" target="_blank" rel="noopener">泥瓦匠BYSocket</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://www.xiongge.club/favicon.ico">
                  </span>
                  <span class="link">
                    <a href="https://www.xiongge.club/" target="_blank" rel="noopener">熊哥club</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/3w4fe.png">
                  </span>
                  <span class="link">
                    <a href="https://zerlong.com/" target="_blank" rel="noopener">知语</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/44hxf.png">
                  </span>
                  <span class="link">
                    <a href="http://redstonewill.com/" target="_blank" rel="noopener">红色石头</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/8g1fk.jpg">
                  </span>
                  <span class="link">
                    <a href="http://www.laodong.me/" target="_blank" rel="noopener">老董博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/wkaus.jpg">
                  </span>
                  <span class="link">
                    <a href="https://zhaoshuai.me/" target="_blank" rel="noopener">碎念</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/pgo0r.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.chenwenguan.com/" target="_blank" rel="noopener">陈文管的博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/kk82a.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.lxlinux.net/" target="_blank" rel="noopener">良许Linux教程网</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/lj0t2.jpg">
                  </span>
                  <span class="link">
                    <a href="https://tanqingbo.cn/" target="_blank" rel="noopener">IT码农</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/i8cdr.png">
                  </span>
                  <span class="link">
                    <a href="https://junyiseo.com/" target="_blank" rel="noopener">均益个人博客</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/chwv2.png">
                  </span>
                  <span class="link">
                    <a href="https://brucedone.com/" target="_blank" rel="noopener">大鱼的鱼塘</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/2y43o.png">
                  </span>
                  <span class="link">
                    <a href="http://bbs.nightteam.cn/" target="_blank" rel="noopener">夜幕爬虫安全论坛</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/zvc3w.jpg">
                  </span>
                  <span class="link">
                    <a href="https://www.weishidong.com/" target="_blank" rel="noopener">韦世东的技术专栏</a>
                  </span>
                </li>
                <li class="friend">
                  <span class="logo">
                    <img src="https://qiniu.cuiqingcai.com/ebudy.jpg">
                  </span>
                  <span class="link">
                    <a href="https://chuanjiabing.com/" target="_blank" rel="noopener">穿甲兵技术社区</a>
                  </span>
                </li>
              </ul>
            </div>
          </div>
        </aside>
        <div id="sidebar-dimmer"></div>
      </div>
    </main>
    <footer class="footer">
      <div class="footer-inner">
        <div class="copyright"> &copy; <span itemprop="copyrightYear">2021</span>
          <span class="with-love">
            <i class="fa fa-heart"></i>
          </span>
          <span class="author" itemprop="copyrightHolder">崔庆才丨静觅</span>
          <span class="post-meta-divider">|</span>
          <span class="post-meta-item-icon">
            <i class="fa fa-chart-area"></i>
          </span>
          <span title="站点总字数">2.6m</span>
          <span class="post-meta-divider">|</span>
          <span class="post-meta-item-icon">
            <i class="fa fa-coffee"></i>
          </span>
          <span title="站点阅读时长">39:54</span>
        </div>
        <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> 强力驱动 </div>
        <div class="beian"><a href="https://beian.miit.gov.cn/" rel="noopener" target="_blank">京ICP备18015597号-1 </a>
        </div>
        <script>
          (function ()
          {
            function leancloudSelector(url)
            {
              url = encodeURI(url);
              return document.getElementById(url).querySelector('.leancloud-visitors-count');
            }

            function addCount(Counter)
            {
              var visitors = document.querySelector('.leancloud_visitors');
              var url = decodeURI(visitors.id);
              var title = visitors.dataset.flagTitle;
              Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify(
              {
                url
              }))).then(response => response.json()).then((
              {
                results
              }) =>
              {
                if (results.length > 0)
                {
                  var counter = results[0];
                  leancloudSelector(url).innerText = counter.time + 1;
                  Counter('put', '/classes/Counter/' + counter.objectId,
                  {
                    time:
                    {
                      '__op': 'Increment',
                      'amount': 1
                    }
                  }).catch(error =>
                  {
                    console.error('Failed to save visitor count', error);
                  });
                }
                else
                {
                  Counter('post', '/classes/Counter',
                  {
                    title,
                    url,
                    time: 1
                  }).then(response => response.json()).then(() =>
                  {
                    leancloudSelector(url).innerText = 1;
                  }).catch(error =>
                  {
                    console.error('Failed to create', error);
                  });
                }
              }).catch(error =>
              {
                console.error('LeanCloud Counter Error', error);
              });
            }

            function showTime(Counter)
            {
              var visitors = document.querySelectorAll('.leancloud_visitors');
              var entries = [...visitors].map(element =>
              {
                return decodeURI(element.id);
              });
              Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify(
              {
                url:
                {
                  '$in': entries
                }
              }))).then(response => response.json()).then((
              {
                results
              }) =>
              {
                for (let url of entries)
                {
                  let target = results.find(item => item.url === url);
                  leancloudSelector(url).innerText = target ? target.time : 0;
                }
              }).catch(error =>
              {
                console.error('LeanCloud Counter Error', error);
              });
            }
            let
            {
              app_id,
              app_key,
              server_url
            } = {
              "enable": true,
              "app_id": "6X5dRQ0pnPWJgYy8SXOg0uID-gzGzoHsz",
              "app_key": "ziLDVEy73ne5HtFTiGstzHMS",
              "server_url": "https://6x5drq0p.lc-cn-n1-shared.com",
              "security": false
            };

            function fetchData(api_server)
            {
              var Counter = (method, url, data) =>
              {
                return fetch(`${api_server}/1.1${url}`,
                {
                  method,
                  headers:
                  {
                    'X-LC-Id': app_id,
                    'X-LC-Key': app_key,
                    'Content-Type': 'application/json',
                  },
                  body: JSON.stringify(data)
                });
              };
              if (CONFIG.page.isPost)
              {
                if (CONFIG.hostname !== location.hostname) return;
                addCount(Counter);
              }
              else if (document.querySelectorAll('.post-title-link').length >= 1)
              {
                showTime(Counter);
              }
            }
            let api_server = app_id.slice(-9) !== '-MdYXbMMI' ? server_url : `https://${app_id.slice(0, 8).toLowerCase()}.api.lncldglobal.com`;
            if (api_server)
            {
              fetchData(api_server);
            }
            else
            {
              fetch('https://app-router.leancloud.cn/2/route?appId=' + app_id).then(response => response.json()).then((
              {
                api_server
              }) =>
              {
                fetchData('https://' + api_server);
              });
            }
          })();

        </script>
      </div>
      <div class="footer-stat">
        <span id="cnzz_stat_icon_1279355174"></span>
        <script type="text/javascript">
          document.write(unescape("%3Cspan id='cnzz_stat_icon_1279355174'%3E%3C/span%3E%3Cscript src='https://v1.cnzz.com/z_stat.php%3Fid%3D1279355174%26online%3D1%26show%3Dline' type='text/javascript'%3E%3C/script%3E"));

        </script>
      </div>
    </footer>
  </div>
  <script src="//cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
  <script src="//cdn.jsdelivr.net/npm/pangu@4/dist/browser/pangu.min.js"></script>
  <script src="/js/utils.js"></script>
  <script src="/.js"></script>
  <script src="/js/schemes/pisces.js"></script>
  <script src="/.js"></script>
  <script src="/js/next-boot.js"></script>
  <script src="/.js"></script>
  <script>
    (function ()
    {
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x = document.getElementsByTagName("link");
      //Find the last canonical URL
      if (x.length > 0)
      {
        for (i = 0; i < x.length; i++)
        {
          if (x[i].rel.toLowerCase() == 'canonical' && x[i].href)
          {
            canonicalURL = x[i].href;
          }
        }
      }
      //Get protocol
      if (!canonicalURL)
      {
        curProtocol = window.location.protocol.split(':')[0];
      }
      else
      {
        curProtocol = canonicalURL.split(':')[0];
      }
      //Get current URL if the canonical URL does not exist
      if (!canonicalURL) canonicalURL = window.location.href;
      //Assign script content. Replace current URL with the canonical URL
      ! function ()
      {
        var e = /([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,
          r = canonicalURL,
          t = document.referrer;
        if (!e.test(r))
        {
          var n = (String(curProtocol).toLowerCase() === 'https') ? "https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif" : "//api.share.baidu.com/s.gif";
          t ? (n += "?r=" + encodeURIComponent(document.referrer), r && (n += "&l=" + r)) : r && (n += "?l=" + r);
          var i = new Image;
          i.src = n
        }
      }(window);
    })();

  </script>
  <script src="/js/local-search.js"></script>
  <script src="/.js"></script>
</body>

</html>
